@o-lang/resolver-tests 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,22 @@
1
+ {
2
+ "test_id": "R-006-runtime-shape",
3
+ "protocol_version": "1.1",
4
+ "category": "resolver-runtime",
5
+ "description": "Ensures resolver exports a callable function.",
6
+ "spec_ref": ["§4.1 Resolver Runtime Interface"],
7
+ "fixtures": {
8
+ "inputs": [
9
+ {
10
+ "invoke": {}
11
+ }
12
+ ]
13
+ },
14
+ "assertions": [
15
+ {
16
+ "id": "resolver_is_function",
17
+ "type": "resolver_is_callable",
18
+ "severity": "fatal",
19
+ "description": "Resolver must be a callable function"
20
+ }
21
+ ]
22
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "test_id": "R-007-failure-contract",
3
+ "protocol_version": "1.1",
4
+ "category": "resolver-runtime",
5
+ "description": "Ensures any thrown failure code is declared in the resolver's failure contract.",
6
+ "spec_ref": ["§4.4 Resolver Failure Modes"],
7
+ "fixtures": {
8
+ "inputs": [
9
+ {
10
+ "invoke": {}
11
+ }
12
+ ]
13
+ },
14
+ "assertions": [
15
+ {
16
+ "id": "failure_code_declared",
17
+ "type": "resolver_failure_declared",
18
+ "severity": "fatal",
19
+ "description": "Any error code thrown by the resolver must be listed in its failures[] declaration"
20
+ }
21
+ ]
22
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "test_id": "R-008-input-validation",
3
+ "protocol_version": "1.1",
4
+ "category": "resolver-runtime",
5
+ "description": "Ensures resolver rejects invocations missing required inputs.",
6
+ "spec_ref": ["§4.2 Resolver Input Contracts"],
7
+ "fixtures": {
8
+ "inputs": [
9
+ {
10
+ "invoke": {}
11
+ }
12
+ ]
13
+ },
14
+ "assertions": [
15
+ {
16
+ "id": "missing_required_input_rejected",
17
+ "type": "rejects_missing_required_input",
18
+ "severity": "fatal",
19
+ "description": "Resolver must fail when a required input is omitted"
20
+ }
21
+ ]
22
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "test_id": "R-009-retry-semantics",
3
+ "protocol_version": "1.1",
4
+ "category": "resolver-runtime",
5
+ "description": "Ensures resolver retry behavior does not exceed declared retry limits when failures occur.",
6
+ "spec_ref": ["§4.4 Resolver Failure Modes"],
7
+ "fixtures": {
8
+ "inputs": [
9
+ {
10
+ "invoke": {}
11
+ }
12
+ ]
13
+ },
14
+ "assertions": [
15
+ {
16
+ "id": "retry_limit_respected",
17
+ "type": "retry_count_within_declared_limit",
18
+ "severity": "fatal",
19
+ "description": "Resolver must not retry more times than declared in its failure specification"
20
+ }
21
+ ]
22
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "test_id": "R-010-output-contract",
3
+ "protocol_version": "1.1",
4
+ "category": "resolver-runtime",
5
+ "description": "Ensures resolver output conforms to its declared output contract.",
6
+ "spec_ref": ["§4.3 Resolver Output Contracts"],
7
+ "fixtures": {
8
+ "inputs": [
9
+ {
10
+ "invoke": {}
11
+ }
12
+ ]
13
+ },
14
+ "assertions": [
15
+ {
16
+ "id": "output_is_object",
17
+ "type": "output_is_object",
18
+ "severity": "fatal",
19
+ "description": "Resolver must return an object"
20
+ },
21
+ {
22
+ "id": "output_fields_match_contract",
23
+ "type": "output_fields_match_contract",
24
+ "severity": "fatal",
25
+ "description": "Returned object must contain all fields declared in outputs[]"
26
+ }
27
+ ]
28
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "test_id": "R-011-determinism",
3
+ "protocol_version": "1.1",
4
+ "category": "resolver-runtime",
5
+ "description": "Ensures resolver produces deterministic output for identical inputs.",
6
+ "spec_ref": ["§5.1 Deterministic Execution"],
7
+ "fixtures": {
8
+ "inputs": [
9
+ {
10
+ "invoke": {}
11
+ }
12
+ ]
13
+ },
14
+ "assertions": [
15
+ {
16
+ "id": "same_input_same_output",
17
+ "type": "deterministic_output",
18
+ "severity": "warning",
19
+ "description": "Identical invocations should produce identical outputs"
20
+ }
21
+ ]
22
+ }
23
+
@@ -0,0 +1,23 @@
1
+ {
2
+ "test_id": "R-012-side-effects",
3
+ "protocol_version": "1.1",
4
+ "category": "resolver-runtime",
5
+ "description": "Ensures resolver does not mutate global state or environment unexpectedly.",
6
+ "spec_ref": ["§5.3 Resolver Isolation"],
7
+ "fixtures": {
8
+ "inputs": [
9
+ {
10
+ "invoke": {}
11
+ }
12
+ ]
13
+ },
14
+ "assertions": [
15
+ {
16
+ "id": "no_global_mutation",
17
+ "type": "no_global_state_mutation",
18
+ "severity": "warning",
19
+ "description": "Resolver should not mutate global objects, process.env, or module-level state"
20
+ }
21
+ ]
22
+ }
23
+
@@ -0,0 +1,18 @@
1
+ <svg xmlns='http://www.w3.org/2000/svg' width='120' height='20'>
2
+ <linearGradient id='b' x2='0' y2='100%'>
3
+ <stop offset='0' stop-color='#bbb' stop-opacity='.1'/>
4
+ <stop offset='1' stop-opacity='.1'/>
5
+ </linearGradient>
6
+ <mask id='a'>
7
+ <rect width='120' height='20' rx='3' fill='#fff'/>
8
+ </mask>
9
+ <g mask='url(#a)'>
10
+ <rect width='70' height='20' fill='#555'/>
11
+ <rect x='70' width='50' height='20' fill='red'/>
12
+ <rect width='120' height='20' fill='url(#b)'/>
13
+ </g>
14
+ <g fill='#fff' text-anchor='middle' font-family='Verdana' font-size='11'>
15
+ <text x='35' y='14'>Certified</text>
16
+ <text x='95' y='14'>fail</text>
17
+ </g>
18
+ </svg>
package/lib/runner.js CHANGED
@@ -1,96 +1,6 @@
1
1
  const path = require("path");
2
2
  const fs = require("fs");
3
3
 
4
- // ----------------------
5
- // Helper: Deep get by path (e.g., "steps[0].saveAs")
6
- // ----------------------
7
- function getNestedValue(obj, path) {
8
- if (!path) return obj;
9
- return path
10
- .split(/\.(?![^\[]*\])|[\[\]]/)
11
- .filter(Boolean)
12
- .reduce((cur, prop) => cur?.[prop], obj);
13
- }
14
-
15
- // ----------------------
16
- // Validator functions for WORKFLOW AST
17
- // ----------------------
18
- function checkAllowlist(ast, expected) {
19
- const allowed = ast.allowedResolvers || [];
20
- return (
21
- Array.isArray(allowed) &&
22
- allowed.length === expected.length &&
23
- expected.every(r => allowed.includes(r))
24
- );
25
- }
26
-
27
- function checkResolverNameNormalization(ast) {
28
- const allowed = ast.allowedResolvers || [];
29
- const pattern = /^[a-zA-Z][a-zA-Z0-9]*$/;
30
- return Array.isArray(allowed) && allowed.every(name => pattern.test(name));
31
- }
32
-
33
- function checkWorkflowName(ast, expected) {
34
- return ast.name === expected;
35
- }
36
-
37
- function checkReturnValues(ast, expected) {
38
- const returns = ast.returnValues || [];
39
- return (
40
- Array.isArray(returns) &&
41
- returns.length === expected.length &&
42
- expected.every(v => returns.includes(v))
43
- );
44
- }
45
-
46
- function checkNoWarnings(status, expectedCount = 0) {
47
- const warnings = status?.__warnings || [];
48
- return warnings.length === expectedCount;
49
- }
50
-
51
- function checkStepType(ast, assertion) {
52
- const steps = ast.steps || [];
53
- const step = steps[assertion.stepIndex];
54
- return step && step.type === assertion.expected;
55
- }
56
-
57
- function checkStepSaveAs(ast, assertion) {
58
- const steps = ast.steps || [];
59
- const step = steps[assertion.stepIndex];
60
- return step && step.saveAs === assertion.expected;
61
- }
62
-
63
- function checkStepFailurePolicies(ast, assertion) {
64
- const steps = ast.steps || [];
65
- const step = steps[assertion.stepIndex];
66
- if (!step || !step.failurePolicies) return false;
67
-
68
- const expected = assertion.expected;
69
- return Object.keys(expected).every(code => {
70
- const policy = step.failurePolicies[code];
71
- return (
72
- policy &&
73
- policy.action === expected[code].action &&
74
- policy.count === expected[code].count
75
- );
76
- });
77
- }
78
-
79
- function checkContainsWarning(status, assertion) {
80
- const warnings = status?.__warnings || [];
81
- const needle = assertion.expected_substring.toLowerCase();
82
- return warnings.some(w =>
83
- (typeof w === "string" ? w : w.message || "")
84
- .toLowerCase()
85
- .includes(needle)
86
- );
87
- }
88
-
89
- function checkStatusGreaterThan(status, assertion) {
90
- const value = getNestedValue(status, assertion.path);
91
- return typeof value === "number" && value > assertion.expected;
92
- }
93
-
94
4
  // ----------------------
95
5
  // Validator functions for RESOLVER METADATA (R-005)
96
6
  // ----------------------
@@ -147,59 +57,71 @@ function checkResolverFailuresValid(resolverMeta) {
147
57
  }
148
58
 
149
59
  // ----------------------
150
- // Assertion handler registry
60
+ // Validator functions for RESOLVER RUNTIME (R-006 → R-012)
151
61
  // ----------------------
152
- const assertionHandlers = {
153
- // Workflow AST assertions
154
- allowed_resolvers_listed: (ast, assertion) =>
155
- checkAllowlist(ast, assertion.expected),
156
-
157
- resolver_names_normalized: ast =>
158
- checkResolverNameNormalization(ast),
159
62
 
160
- workflow_name_present: (ast, assertion) =>
161
- checkWorkflowName(ast, assertion.expected),
162
-
163
- workflow_return_values: (ast, assertion) =>
164
- checkReturnValues(ast, assertion.expected),
63
+ function checkResolverIsCallable(resolver) {
64
+ return typeof resolver === 'function';
65
+ }
165
66
 
166
- workflow_return_values_empty: (ast, assertion) =>
167
- checkReturnValues(ast, assertion.expected),
67
+ function checkFailureCodeDeclared(observedError, resolverMeta) {
68
+ if (!observedError?.code) return true; // no error = pass
69
+ const declaredCodes = (resolverMeta.failures || []).map(f => f.code);
70
+ return declaredCodes.includes(observedError.code);
71
+ }
168
72
 
169
- no_parse_warnings: (ast, assertion, status) =>
170
- checkNoWarnings(status, assertion.expected),
73
+ function checkRejectsMissingRequiredInput(invocationResult) {
74
+ return invocationResult.threw; // must throw when required input missing
75
+ }
171
76
 
172
- step_type: (ast, assertion) =>
173
- checkStepType(ast, assertion),
77
+ function checkRetryCountWithinLimit(observedRetries, resolverMeta, errorCode) {
78
+ const failure = (resolverMeta.failures || []).find(f => f.code === errorCode);
79
+ if (!failure) return true;
80
+ return observedRetries <= failure.retries;
81
+ }
174
82
 
175
- step_saveas: (ast, assertion) =>
176
- checkStepSaveAs(ast, assertion),
83
+ function checkOutputIsObject(output) {
84
+ return output !== null && typeof output === 'object' && !Array.isArray(output);
85
+ }
177
86
 
178
- step_failure_policies: (ast, assertion) =>
179
- checkStepFailurePolicies(ast, assertion),
87
+ function checkOutputFieldsMatchContract(output, resolverMeta) {
88
+ const declaredNames = (resolverMeta.outputs || []).map(o => o.name);
89
+ return declaredNames.every(name => name in output);
90
+ }
180
91
 
181
- // Negative test assertions (R-004)
182
- contains_warning: (ast, assertion, status) =>
183
- checkContainsWarning(status, assertion),
92
+ function checkDeterministicOutput(results) {
93
+ if (results.length < 2) return true;
94
+ const first = JSON.stringify(results[0]);
95
+ return results.slice(1).every(r => JSON.stringify(r) === first);
96
+ }
184
97
 
185
- status_greater_than: (ast, assertion, status) =>
186
- checkStatusGreaterThan(status, assertion),
98
+ function checkNoGlobalMutation() {
99
+ // Placeholder: real impl would compare global state snapshots
100
+ return true;
101
+ }
187
102
 
188
- // Resolver metadata assertions (R-005)
103
+ // ----------------------
104
+ // Assertion handler registry.
105
+ // ----------------------
106
+ const assertionHandlers = {
107
+ // R-005: Metadata
189
108
  resolver_has_field: (resolverMeta, assertion) =>
190
109
  checkResolverHasField(resolverMeta, assertion),
191
-
192
- resolver_inputs_valid: resolverMeta =>
193
- checkResolverInputsValid(resolverMeta),
194
-
195
- resolver_outputs_valid: resolverMeta =>
196
- checkResolverOutputsValid(resolverMeta),
197
-
198
- field_names_normalized: (resolverMeta, assertion) =>
199
- checkFieldNamesNormalized(resolverMeta, assertion),
200
-
201
- resolver_failures_valid: resolverMeta =>
202
- checkResolverFailuresValid(resolverMeta),
110
+ resolver_inputs_valid: checkResolverInputsValid,
111
+ resolver_outputs_valid: checkResolverOutputsValid,
112
+ field_names_normalized: checkFieldNamesNormalized,
113
+ resolver_failures_valid: checkResolverFailuresValid,
114
+
115
+ // R-006–R-012: Runtime
116
+ resolver_is_callable: (ctx) => checkResolverIsCallable(ctx.resolver),
117
+ resolver_failure_declared: (ctx) => checkFailureCodeDeclared(ctx.error, ctx.resolverMeta),
118
+ rejects_missing_required_input: (ctx) => checkRejectsMissingRequiredInput(ctx),
119
+ retry_count_within_declared_limit: (ctx) =>
120
+ checkRetryCountWithinLimit(ctx.retryCount, ctx.resolverMeta, ctx.error?.code),
121
+ output_is_object: (ctx) => checkOutputIsObject(ctx.output),
122
+ output_fields_match_contract: (ctx) => checkOutputFieldsMatchContract(ctx.output, ctx.resolverMeta),
123
+ deterministic_output: (ctx) => checkDeterministicOutput(ctx.outputs),
124
+ no_global_state_mutation: () => checkNoGlobalMutation(),
203
125
  };
204
126
 
205
127
  // ----------------------
@@ -241,20 +163,58 @@ function runAssertions(testSpec, target, status = {}) {
241
163
  message:
242
164
  failures.length === 0
243
165
  ? "All assertions passed"
244
- : failures
245
- .map(f => `[${f.severity}] ${f.id}: ${f.message}`)
246
- .join("; "),
166
+ : failures.map(f => `[${f.severity}] ${f.id}: ${f.message}`).join("; "),
247
167
  failures,
248
168
  };
249
169
  }
250
170
 
171
+ // ----------------------
172
+ // Runtime resolver invoker with observation
173
+ // ----------------------
174
+ async function invokeResolverWithObservation(resolver, resolverMeta, testSpec) {
175
+ const ctx = {
176
+ resolver,
177
+ resolverMeta,
178
+ output: null,
179
+ outputs: [],
180
+ error: null,
181
+ threw: false,
182
+ retryCount: 0,
183
+ };
184
+
185
+ // Use empty input — resolver must handle its own validation
186
+ const input = {};
187
+
188
+ // Run multiple times for determinism test
189
+ const runs = testSpec.test_id === 'R-011-determinism' ? 3 : 1;
190
+
191
+ for (let i = 0; i < runs; i++) {
192
+ try {
193
+ const result = await Promise.resolve(resolver(input));
194
+ ctx.output = result;
195
+ ctx.outputs.push(result);
196
+ } catch (err) {
197
+ ctx.threw = true;
198
+ ctx.error = err;
199
+ // Simple retry count inference (real impl would use retry loop)
200
+ if (err.code) {
201
+ const decl = resolverMeta.failures?.find(f => f.code === err.code);
202
+ if (decl) ctx.retryCount = decl.retries;
203
+ }
204
+ break; // stop on first error for non-determinism tests
205
+ }
206
+ }
207
+
208
+ return ctx;
209
+ }
210
+
251
211
  // ----------------------
252
212
  // Test suite executor
253
213
  // ----------------------
254
214
  async function runAllTests({ suites, resolver }) {
255
215
  let failed = 0;
256
- // Resolve test suites from the package root (where R-001-..., R-005-... live)
257
216
  const PACKAGE_ROOT = path.join(__dirname, '..');
217
+ const resolverMeta = resolver.resolverDeclaration || resolver;
258
218
 
259
219
  for (const suite of suites) {
260
220
  const suiteDir = path.join(PACKAGE_ROOT, suite);
@@ -267,65 +227,52 @@ async function runAllTests({ suites, resolver }) {
267
227
  }
268
228
 
269
229
  const testSpec = JSON.parse(fs.readFileSync(testSpecPath, "utf8"));
270
- let target;
271
- let status = {};
272
-
273
230
  const fixture = testSpec.fixtures.inputs[0];
274
231
 
275
- if (fixture.workflow) {
276
- // Workflow parsing test (R-001 R-004)
277
- const workflowPath = path.join(suiteDir, fixture.workflow);
278
- if (!fs.existsSync(workflowPath)) {
279
- console.error(`❌ Workflow file missing: ${workflowPath}`);
232
+ if (fixture.resolver_contract) {
233
+ // R-005: Validate metadata structure
234
+ const contractPath = path.join(suiteDir, fixture.resolver_contract);
235
+ if (!fs.existsSync(contractPath)) {
236
+ console.error(`❌ Resolver contract missing: ${contractPath}`);
280
237
  failed++;
281
238
  continue;
282
239
  }
283
240
 
284
- const workflowSource = fs.readFileSync(workflowPath, "utf8");
241
+ let target;
285
242
  try {
286
- const parseResult = resolver.parse
287
- ? resolver.parse(workflowSource)
288
- : { ast: resolver };
289
-
290
- target = parseResult.ast;
291
- status = { __warnings: parseResult.__warnings || [] };
243
+ target = require(contractPath);
292
244
  } catch (err) {
293
- console.error(`❌ Parse failed for ${suite}:`, err.message);
245
+ console.error(`❌ Failed to load resolver contract ${suite}:`, err.message);
294
246
  failed++;
295
247
  continue;
296
248
  }
297
- } else if (fixture.resolver_contract) {
298
- // Resolver metadata test (R-005)
299
- const contractPath = path.join(suiteDir, fixture.resolver_contract);
300
- if (!fs.existsSync(contractPath)) {
301
- console.error(`❌ Resolver contract missing: ${contractPath}`);
249
+
250
+ const result = runAssertions(testSpec, target);
251
+ if (!result.ok) {
252
+ console.error(`❌ ${suite} failed: ${result.message}`);
302
253
  failed++;
303
- continue;
254
+ } else {
255
+ console.log(`✅ ${suite} passed`);
304
256
  }
305
-
257
+ } else if (testSpec.category === "resolver-runtime") {
258
+ // R-006 → R-012: Observe real resolver behavior
306
259
  try {
307
- target = require(contractPath);
260
+ const runtimeContext = await invokeResolverWithObservation(resolver, resolverMeta, testSpec);
261
+ const result = runAssertions(testSpec, runtimeContext);
262
+
263
+ if (!result.ok) {
264
+ console.error(`❌ ${suite} failed: ${result.message}`);
265
+ failed++;
266
+ } else {
267
+ console.log(`✅ ${suite} passed`);
268
+ }
308
269
  } catch (err) {
309
- console.error(
310
- `❌ Failed to load resolver contract ${suite}:`,
311
- err.message
312
- );
270
+ console.error(`🔥 Runtime test ${suite} crashed:`, err.message);
313
271
  failed++;
314
- continue;
315
272
  }
316
273
  } else {
317
274
  console.error(`❌ Unrecognized fixture in ${suite}`);
318
275
  failed++;
319
- continue;
320
- }
321
-
322
- const result = runAssertions(testSpec, target, status);
323
-
324
- if (!result.ok) {
325
- console.error(`❌ ${suite} failed: ${result.message}`);
326
- failed++;
327
- } else {
328
- console.log(`✅ ${suite} passed`);
329
276
  }
330
277
  }
331
278
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@o-lang/resolver-tests",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Official O-Lang Resolver Test Harness — locked single entrypoint",
5
5
  "main": "run.js",
6
6
  "type": "commonjs",
package/run.js CHANGED
@@ -72,10 +72,14 @@ const { generateBadge } = require("./lib/badge");
72
72
  opts.suites.length > 0
73
73
  ? opts.suites
74
74
  : [
75
- "R-001-allowlist",
76
- "R-002-io-contract",
77
- "R-003-failure-modes",
78
- "R-005-resolver-metadata-contract"
75
+ "R-005-resolver-metadata-contract",
76
+ "R-006-runtime-shape",
77
+ "R-007-failure-contract",
78
+ "R-008-input-validation",
79
+ "R-009-retry-semantics",
80
+ "R-010-output-contract",
81
+ "R-011-determinism",
82
+ "R-012-side-effects"
79
83
  ];
80
84
 
81
85
  const result = await runAllTests({
@@ -1,21 +0,0 @@
1
- # R-001 Allowlist Resolver Test
2
-
3
- **Purpose:**
4
- This test verifies that the O-Lang parser correctly captures and normalizes the list of allowed resolvers declared in a workflow.
5
-
6
- **Workflow:**
7
- - Workflow Name: `TestResolverAllowlist`
8
- - Allowed Resolvers:
9
- - `SimpleResolver`
10
- - `AdvancedResolver`
11
-
12
- **Assertions:**
13
- 1. All allowed resolvers are captured correctly.
14
- 2. Resolver names are normalized (no spaces, invalid characters).
15
- 3. Workflow name is correctly parsed.
16
- 4. No parser warnings are generated for valid syntax.
17
-
18
- **Spec References:**
19
- - §4.2 Allowed Resolvers
20
- - §3.1 Workflow Parsing
21
- - §2.3 Symbol Normalization
@@ -1,55 +0,0 @@
1
- {
2
- "test_id": "R-001-allowlist",
3
- "protocol_version": "1.1",
4
- "category": "resolver",
5
- "description": "Tests that the parser correctly captures and normalizes allowed resolvers.",
6
- "spec_ref": [
7
- "§4.2 Allowed Resolvers",
8
- "§3.1 Workflow Parsing",
9
- "§2.3 Symbol Normalization"
10
- ],
11
- "fixtures": {
12
- "inputs": [
13
- {
14
- "workflow": "workflow.ol",
15
- "values": {}
16
- }
17
- ]
18
- },
19
- "assertions": [
20
- {
21
- "id": "allowed_resolvers_listed",
22
- "type": "allowed_resolvers_listed",
23
- "expected": ["SimpleResolver", "AdvancedResolver"],
24
- "severity": "fatal",
25
- "description": "Parser must capture all allowed resolvers"
26
- },
27
- {
28
- "id": "resolver_names_normalized",
29
- "type": "resolver_names_normalized",
30
- "severity": "fatal",
31
- "description": "All resolver names must conform to symbol normalization rules (start with letter, alphanumeric only)"
32
- },
33
- {
34
- "id": "workflow_name_present",
35
- "type": "workflow_name_present",
36
- "expected": "TestResolverAllowlist",
37
- "severity": "fatal",
38
- "description": "Workflow name must be parsed correctly"
39
- },
40
- {
41
- "id": "workflow_return_values_empty",
42
- "type": "workflow_return_values_empty",
43
- "expected": [],
44
- "severity": "fatal",
45
- "description": "Return values must be empty as per workflow"
46
- },
47
- {
48
- "id": "no_parse_warnings",
49
- "type": "no_parse_warnings",
50
- "expected": 0,
51
- "severity": "warning",
52
- "description": "No warnings should be issued for valid syntax"
53
- }
54
- ]
55
- }
@@ -1,7 +0,0 @@
1
- Workflow "TestResolverAllowlist"
2
-
3
- Allow resolvers:
4
- - SimpleResolver
5
- - AdvancedResolver
6
-
7
- Return
@@ -1,26 +0,0 @@
1
- # R-002 IO Contract Resolver Test
2
-
3
- **Purpose:**
4
- This test validates that resolver input/output contracts are correctly recognized, including the proper handling of `Ask` and `Use` steps, and that `saveAs` values are normalized.
5
-
6
- **Workflow:**
7
- - Workflow Name: `TestResolverIOContract`
8
- - Parameters: `userInput`
9
- - Allowed Resolvers:
10
- - `Calculator`
11
- - `WeatherService`
12
- - Steps:
13
- 1. `Ask Calculator` → `saveAs: calcResult`
14
- 2. `Use WeatherService` → `saveAs: weatherInfo`
15
- - Return Values: `calcResult, weatherInfo`
16
-
17
- **Assertions:**
18
- 1. Step 1 is parsed as an `ask` step with normalized `saveAs`.
19
- 2. Step 2 is parsed as a `use` step with normalized `saveAs`.
20
- 3. Return statement lists all resolver outputs.
21
- 4. No parser warnings are generated.
22
-
23
- **Spec References:**
24
- - §4.3 Resolver Input/Output Contracts
25
- - §3.2 Step Parsing
26
- - §2.3 Symbol Normalization
@@ -1,76 +0,0 @@
1
- {
2
- "test_id": "R-002-io-contract",
3
- "protocol_version": "1.1",
4
- "category": "resolver",
5
- "description": "Tests that resolver I/O contracts are correctly recognized and saveAs fields are applied.",
6
- "spec_ref": [
7
- "§4.3 Resolver Input/Output Contracts",
8
- "§3.2 Step Parsing",
9
- "§2.3 Symbol Normalization"
10
- ],
11
- "fixtures": {
12
- "inputs": [
13
- {
14
- "workflow": "workflow.ol",
15
- "values": {
16
- "userInput": "42"
17
- }
18
- }
19
- ]
20
- },
21
- "assertions": [
22
- {
23
- "id": "allowed_resolvers_listed",
24
- "type": "allowed_resolvers_listed",
25
- "expected": ["Calculator", "WeatherService"],
26
- "severity": "fatal",
27
- "description": "Parser must capture all allowed resolvers"
28
- },
29
- {
30
- "id": "ask_step_type",
31
- "type": "step_type",
32
- "stepIndex": 0,
33
- "expected": "ask",
34
- "severity": "fatal",
35
- "description": "Step 1 must be parsed as 'ask'"
36
- },
37
- {
38
- "id": "ask_step_saveas",
39
- "type": "step_saveas",
40
- "stepIndex": 0,
41
- "expected": "calcResult",
42
- "severity": "fatal",
43
- "description": "Step 1 saveAs must be 'calcResult'"
44
- },
45
- {
46
- "id": "use_step_type",
47
- "type": "step_type",
48
- "stepIndex": 1,
49
- "expected": "use",
50
- "severity": "fatal",
51
- "description": "Step 2 must be parsed as 'use'"
52
- },
53
- {
54
- "id": "use_step_saveas",
55
- "type": "step_saveas",
56
- "stepIndex": 1,
57
- "expected": "weatherInfo",
58
- "severity": "fatal",
59
- "description": "Step 2 saveAs must be 'weatherInfo'"
60
- },
61
- {
62
- "id": "workflow_return_values",
63
- "type": "workflow_return_values",
64
- "expected": ["calcResult", "weatherInfo"],
65
- "severity": "fatal",
66
- "description": "Return statement must list all saved outputs"
67
- },
68
- {
69
- "id": "no_parse_warnings",
70
- "type": "no_parse_warnings",
71
- "expected": 0,
72
- "severity": "warning",
73
- "description": "No warnings for valid I/O contracts"
74
- }
75
- ]
76
- }
@@ -1,13 +0,0 @@
1
- Workflow "TestResolverIOContract" with userInput
2
-
3
- Allow resolvers:
4
- - Calculator
5
- - WeatherService
6
-
7
- Step 1: Ask Calculator
8
- Save as calcResult
9
-
10
- Step 2: Use WeatherService
11
- Save as weatherInfo
12
-
13
- Return calcResult, weatherInfo
@@ -1,27 +0,0 @@
1
- # R-003 Failure Modes Resolver Test
2
-
3
- **Purpose:**
4
- This test validates that resolver failures, missing resolvers, and runtime errors are correctly handled and surfaced by the parser/runtime.
5
-
6
- **Workflow:**
7
- - Workflow Name: `TestResolverFailureModes`
8
- - Parameters: `userInput`
9
- - Allowed Resolvers:
10
- - `ReliableResolver`
11
- - `UnstableResolver`
12
- - Steps:
13
- 1. `Ask ReliableResolver` → `saveAs: reliableResult`
14
- 2. `Ask UnstableResolver` → `saveAs: unstableResult`
15
- - Return Values: `reliableResult, unstableResult`
16
-
17
- **Assertions:**
18
- 1. Steps 1 & 2 are correctly parsed as `ask` resolver steps.
19
- 2. `saveAs` values are normalized.
20
- 3. Return statement lists all resolver outputs.
21
- 4. Errors from failing resolvers (e.g., `UnstableResolver`) are surfaced.
22
- 5. Missing resolvers (e.g., `MissingResolver`) trigger proper errors.
23
-
24
- **Spec References:**
25
- - §4.4 Resolver Failure Modes
26
- - §3.2 Step Parsing
27
- - §5.1 Error Handling
@@ -1,78 +0,0 @@
1
- {
2
- "test_id": "R-003-failure-modes",
3
- "protocol_version": "1.1",
4
- "category": "resolver",
5
- "description": "Tests that resolver failure modes and retry policies are parsed and exposed.",
6
- "spec_ref": [
7
- "§4.4 Resolver Failure Modes",
8
- "§3.3 Step Parsing",
9
- "§2.3 Symbol Normalization"
10
- ],
11
- "fixtures": {
12
- "inputs": [
13
- {
14
- "workflow": "workflow.ol",
15
- "values": {
16
- "userInput": "someInput"
17
- }
18
- }
19
- ]
20
- },
21
- "assertions": [
22
- {
23
- "id": "allowed_resolvers_listed",
24
- "type": "allowed_resolvers_listed",
25
- "expected": ["ReliableResolver", "UnstableResolver"],
26
- "severity": "fatal",
27
- "description": "Allowed resolvers must be captured"
28
- },
29
- {
30
- "id": "step_0_saveas",
31
- "type": "step_saveas",
32
- "stepIndex": 0,
33
- "expected": "reliableResult",
34
- "severity": "fatal",
35
- "description": "Step 0 saveAs"
36
- },
37
- {
38
- "id": "step_1_saveas",
39
- "type": "step_saveas",
40
- "stepIndex": 1,
41
- "expected": "unstableResult",
42
- "severity": "fatal",
43
- "description": "Step 1 saveAs"
44
- },
45
- {
46
- "id": "workflow_name_present",
47
- "type": "workflow_name_present",
48
- "expected": "TestResolverFailureModes",
49
- "severity": "fatal",
50
- "description": "Workflow name parsed"
51
- },
52
- {
53
- "id": "workflow_return_values",
54
- "type": "workflow_return_values",
55
- "expected": ["reliableResult", "unstableResult"],
56
- "severity": "fatal",
57
- "description": "Return values listed"
58
- },
59
- {
60
- "id": "unstable_resolver_failure_policies",
61
- "type": "step_failure_policies",
62
- "stepIndex": 1,
63
- "expected": {
64
- "TIMEOUT": { "action": "retry", "count": 2 },
65
- "NETWORK_ERROR": { "action": "fail", "count": 0 }
66
- },
67
- "severity": "fatal",
68
- "description": "Failure modes and retry counts must be parsed"
69
- },
70
- {
71
- "id": "no_parse_warnings",
72
- "type": "no_parse_warnings",
73
- "expected": 0,
74
- "severity": "warning",
75
- "description": "No warnings for valid failure mode syntax"
76
- }
77
- ]
78
- }
@@ -1,13 +0,0 @@
1
- Workflow "TestResolverFailureModes" with userInput
2
-
3
- Allow resolvers:
4
- - ReliableResolver
5
- - UnstableResolver
6
-
7
- Step 1: Ask ReliableResolver
8
- Save as reliableResult
9
-
10
- Step 2: Ask UnstableResolver
11
- Save as unstableResult
12
-
13
- Return reliableResult, unstableResult
@@ -1,5 +0,0 @@
1
- # R-005: Resolver Metadata Contract
2
-
3
- Validates that resolver packages declare valid **inputs**, **outputs**, **resolverName**, and **failure modes**.
4
-
5
- This test ensures that static resolver contracts (e.g., `resolver.js`) are well-formed and align with O-lang’s governance model—enabling tooling, linting, and safe composition.
@@ -1,57 +0,0 @@
1
- {
2
- "test_id": "R-004-invalid-syntax",
3
- "protocol_version": "1.1",
4
- "category": "resolver",
5
- "description": "Tests that parser emits warnings or errors for invalid syntax.",
6
- "spec_ref": [
7
- "§2.3 Symbol Normalization",
8
- "§3.1 Workflow Parsing",
9
- "§4.2 Allowed Resolvers"
10
- ],
11
- "fixtures": {
12
- "inputs": [
13
- {
14
- "workflow": "workflow.ol",
15
- "values": {}
16
- }
17
- ]
18
- },
19
- "assertions": [
20
- {
21
- "id": "warns_on_invalid_resolver_name",
22
- "type": "contains_warning",
23
- "expected_substring": "invalid resolver name",
24
- "severity": "fatal",
25
- "description": "Must warn about resolver name with space"
26
- },
27
- {
28
- "id": "warns_on_invalid_saveas",
29
- "type": "contains_warning",
30
- "expected_substring": "invalid identifier",
31
- "severity": "fatal",
32
- "description": "Must warn about malformed Save as"
33
- },
34
- {
35
- "id": "warns_on_unrecognized_step",
36
- "type": "contains_warning",
37
- "expected_substring": "unrecognized step",
38
- "severity": "fatal",
39
- "description": "Must warn about unknown step type"
40
- },
41
- {
42
- "id": "warns_on_duplicate_saveas",
43
- "type": "contains_warning",
44
- "expected_substring": "duplicate saveAs",
45
- "severity": "fatal",
46
- "description": "Must warn about duplicate Save as names"
47
- },
48
- {
49
- "id": "parse_status_has_warnings",
50
- "type": "status_greater_than",
51
- "path": "__warnings.length",
52
- "expected": 0,
53
- "severity": "fatal",
54
- "description": "Parser must emit at least one warning"
55
- }
56
- ]
57
- }
@@ -1,16 +0,0 @@
1
- // resolver.ol (treated as a JS module in test)
2
- module.exports = {
3
- resolverName: "RiskAssessment",
4
- inputs: [
5
- { name: "transaction_id", type: "string", required: true },
6
- { name: "user_id", type: "string", required: true }
7
- ],
8
- outputs: [
9
- { name: "risk_score", type: "number" },
10
- { name: "confidence", type: "number" }
11
- ],
12
- failures: [
13
- { code: "DATA_UNAVAILABLE", retries: 1 },
14
- { code: "MODEL_ERROR", retries: 0 }
15
- ]
16
- };
package/certified.svg DELETED
@@ -1,6 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="160" height="28">
2
- <rect rx="4" width="160" height="28" fill="#2d2d2d"/>
3
- <rect rx="4" x="80" width="80" height="28" fill="#4cbb17"/>
4
- <text x="40" y="18" fill="#fff" font-size="13" font-family="Arial" text-anchor="middle">O-lang</text>
5
- <text x="120" y="18" fill="#fff" font-size="13" font-family="Arial" text-anchor="middle">CERTIFIED</text>
6
- </svg>