@syncmatters/script-api 1.0.19 → 1.0.21

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syncmatters/script-api",
3
- "version": "1.0.19",
3
+ "version": "1.0.21",
4
4
  "description": "TypeScript type definitions for the SyncMatters script API (types only - scripts execute on the SyncMatters platform)",
5
5
  "types": "./index.d.ts",
6
6
  "exports": {
@@ -28,7 +28,7 @@
28
28
  "license": "MIT",
29
29
  "author": "SyncMatters",
30
30
  "homepage": "https://syncmatters.com",
31
- "typesContentHash": "c99cda04200f9c6c8b5d28f7433f84607e3b6ddab34d4ca5ae6ce081effbf310",
31
+ "typesContentHash": "3dcf1af8c79b37f9fe75992f52dbbdc70d0f22515d9f65b1123ceb7e4f31de20",
32
32
  "dependencies": {
33
33
  "@types/node": "*"
34
34
  }
@@ -54,7 +54,8 @@ export interface TestFeatureProvider {
54
54
  /**
55
55
  * Prepare data that can be used to test a queries with 'rowFilters'; you may either create new test data, or search/find
56
56
  * data that can be used to test the various row filters. Include one test of of test data for each row filter. Returning
57
- * undefined indicates that row filters cannot be tested.
57
+ * undefined indicates that row filters cannot be tested (the rowFilter feature then reads Not tested, which does not
58
+ * make the object Partial: row filters have no ObjectFeatures flag to opt in with).
58
59
  */
59
60
  rowFiltersPrepare?(options: RowFiltersOptions): Promise<Array<RowFilterData> | undefined>;
60
61
  /** Delete test data created for use by a test. */
@@ -68,10 +69,52 @@ export interface TestFeatureProvider {
68
69
  upsertPrepare?(options: UpsertOptions): Promise<UpsertData | undefined>;
69
70
  /** Delete test data created for use by a test. */
70
71
  upsertCleanup?(options: UpsertCleanupOptions): Promise<void>;
72
+ /**
73
+ * Named checks that are not shaped like an object feature (a free statement, connection churn, a connection-level
74
+ * timeout). They run after the feature tests, in declaration order, each with its own status, duration and details.
75
+ * An escape hatch: a check that recurs across connectors belongs in the harness as a feature.
76
+ */
77
+ customChecks?(): Promise<CustomCheck[]>;
71
78
  /** Optional teardown function to run after all tests have completed. Can perform any required teardown, such as deleting test
72
79
  * data. */
73
80
  after?(): Promise<void>;
74
81
  }
82
+ /** One named check run by the harness after the feature tests (TestFeatureProvider.customChecks()). */
83
+ export interface CustomCheck {
84
+ /** stable id: keys the check's result and details (details["check:<id>"]) and is what --check names */
85
+ id: string;
86
+ /** display name for results */
87
+ name: string;
88
+ /**
89
+ * The object the check exercises. Gated like a feature: when the object is not on the connection (not selected on a
90
+ * two-phase connection, or not returned by meta()) the check reads Not tested with the reason, and its result is
91
+ * reported under that object. A check without one is connection-level and always runs.
92
+ */
93
+ objectId?: string;
94
+ /** default 60 000; capped at the run's remaining budget when the run has one */
95
+ timeoutMs?: number;
96
+ /** throw to fail; ctx.record(message, data) adds details. A check past its timeout fails and the suite carries on. */
97
+ run(ctx: CustomCheckContext): Promise<void>;
98
+ }
99
+ /** What a custom check runs with. */
100
+ export interface CustomCheckContext {
101
+ /** the connection under test, as before() receives it */
102
+ connection: unknown;
103
+ /** metadata of the check's object, when it has one */
104
+ meta?: API.ObjectMeta;
105
+ log: API.Logger;
106
+ /** record a detail on the check's result (logged too), under the same size caps as every feature's details */
107
+ record(message: string, data?: unknown): void;
108
+ }
109
+ /** Result of one custom check. Its details are in the owning result's details under "check:<id>". */
110
+ export interface CustomCheckResult {
111
+ id: string;
112
+ name: string;
113
+ status: "Passed" | "Not tested" | `Failed: ${string}`;
114
+ notTestedReason?: string;
115
+ durationMs: number;
116
+ fatalError?: CaughtError;
117
+ }
75
118
  /** Specification of features that are supported by an object; much of this is auto generated however particular
76
119
  * features may be further refined/specified by the test harness.
77
120
  */
@@ -130,6 +173,17 @@ export interface ObjectFeatures {
130
173
  relationshipsToSameAsObjectId?: string;
131
174
  /** This option is as-per the object level cannotTestReason, however applied to all relationship operations */
132
175
  relationshipsToCannotTestReason?: string;
176
+ /**
177
+ * The query field whose value becomes row.meta.key (generated like upsert.key). When declared, the fieldMetadata
178
+ * feature checks that the field is tagged isKey (isUserKey when expectUserKey) and that no other query field is, and
179
+ * the list and idsFilter features check that every returned row's meta.key equals the string form of its value.
180
+ */
181
+ key?: {
182
+ /** Path to the key in query fields */
183
+ path?: API.JsonValuePath;
184
+ /** Is the key field tagged isUserKey (a user supplied key) rather than isKey? */
185
+ expectUserKey?: boolean;
186
+ };
133
187
  };
134
188
  upsert?: {
135
189
  /** This option is as-per the object level sameAsObjectId, however applied at the upsert operation */
@@ -208,10 +262,12 @@ export interface ConnectorTestResult {
208
262
  testConnection?: "Passed" | "Not tested" | "Failed: Fatal error during testing" | "Failed: Test connection response did not report success";
209
263
  objectsStatus?: "Passed" | "Partial: Only selected objects tested" | "Partial: Only selected objects tested, not all features can be tested" | "Partial: Only selected object tests run" | "Partial: Only selected objects tested, only selected object tests run" | "Partial: Not all features/objects can be tested" | "Not tested" | "Failed: Fatal error during testing" | "Failed: One or more objects failed tests";
210
264
  objects: ObjectTestResult[];
211
- /** Diagnostic details for connection-level stages, keyed by stage (testConnection, metaRefresh). */
265
+ /** Diagnostic details for connection-level stages, keyed by stage (testConnection, metaRefresh) and check:<id>. */
212
266
  details?: {
213
267
  [stage: string]: TestDetail[];
214
268
  };
269
+ /** Connection-level custom checks (no objectId), in declaration order. */
270
+ customChecks?: CustomCheckResult[];
215
271
  }
216
272
  export interface ObjectTestResult {
217
273
  objectId: string;
@@ -219,15 +275,15 @@ export interface ObjectTestResult {
219
275
  fatalError?: CaughtError;
220
276
  notTestedSameAsObjectId?: string;
221
277
  notTestedReason?: string;
222
- list?: "Passed" | "Passed: Limited test data, pagination not tested" | "Not tested" | "Not supported" | "Failed: 'Same as object' not tested" | "Failed: Object metadata supports list, test spec does not" | "Failed: Test spec supports list, object metadata does not" | "Failed: Fatal error during testing" | "Failed: No test data";
278
+ list?: "Passed" | "Passed: Limited test data, pagination not tested" | "Not tested" | "Not supported" | "Failed: 'Same as object' not tested" | "Failed: Object metadata supports list, test spec does not" | "Failed: Test spec supports list, object metadata does not" | "Failed: Fatal error during testing" | "Failed: No test data" | "Failed: Expected values did not match" | "Failed: Expected error did not occur";
223
279
  listNotTestedSameAsObjectId?: string;
224
280
  listNotTestedReason?: string;
225
281
  listFatalError?: CaughtError;
226
- idsFilter?: "Passed" | "Not tested" | "Not supported" | "Failed: 'Same as object' not tested" | "Failed: Object metadata supports idsFilter, test spec does not" | "Failed: Test spec supports idsFilter, object metadata does not" | "Failed: Fatal error during testing" | "Failed: No test data" | "Failed: Returned unexpected row" | "Failed: Row not returned" | "Failed: More rows returned than expected";
282
+ idsFilter?: "Passed" | "Not tested" | "Not supported" | "Failed: 'Same as object' not tested" | "Failed: Object metadata supports idsFilter, test spec does not" | "Failed: Test spec supports idsFilter, object metadata does not" | "Failed: Fatal error during testing" | "Failed: No test data" | "Failed: Returned unexpected row" | "Failed: Row not returned" | "Failed: More rows returned than expected" | "Failed: Expected values did not match" | "Failed: Expected error did not occur";
227
283
  idsFilterNotTestedSameAsObjectId?: string;
228
284
  idsFilterNotTestedReason?: string;
229
285
  idsFilterFatalError?: CaughtError;
230
- checkpointFilter?: "Passed" | "Not tested" | "Not supported" | "Failed: 'Same as object' not tested" | "Failed: Object metadata supports checkpointFilter, test spec does not" | "Failed: Test spec supports checkpointFilter, object metadata does not" | "Failed: Fatal error during testing" | "Failed: No test data for step 1" | "Failed: No test data for step 2" | "Failed: Returned unexpected row for step 1" | "Failed: Returned unexpected row for step 2" | "Failed: Row not returned for step 1" | "Failed: Row not returned for step 2" | "Failed: Next checkpoint not returned";
286
+ checkpointFilter?: "Passed" | "Not tested" | "Not supported" | "Failed: 'Same as object' not tested" | "Failed: Object metadata supports checkpointFilter, test spec does not" | "Failed: Test spec supports checkpointFilter, object metadata does not" | "Failed: Fatal error during testing" | "Failed: No test data for step 1" | "Failed: No test data for step 2" | "Failed: Returned unexpected row for step 1" | "Failed: Returned unexpected row for step 2" | "Failed: Row not returned for step 1" | "Failed: Row not returned for step 2" | "Failed: Next checkpoint not returned" | "Failed: Expected values did not match" | "Failed: Expected error did not occur";
231
287
  checkpointFilterNotTestedSameAsObjectId?: string;
232
288
  checkpointFilterNotTestedReason?: string;
233
289
  checkpointFilterFatalError?: CaughtError;
@@ -240,7 +296,7 @@ export interface ObjectTestResult {
240
296
  matchFilterNotTestedReason?: string;
241
297
  matchFieldCount?: number;
242
298
  matchFilterResults?: MatchRuleTestResult[];
243
- upsert?: "Passed" | "Passed (insert only)" | "Not tested" | "Not supported" | "Failed: 'Same as object' not tested" | "Failed: Feature has no test spec" | "Failed: Fatal error during testing" | "Failed: Test spec does not define key field" | "Failed: No test data for insert" | "Failed: No test data for update" | "Failed: No verifyField included with test data" | "Failed: query after insert did not return expected rows" | "Failed: No key path to test update" | "Failed: update returned unexpected row" | "Failed: verifyField value on the upsert row was blank" | "Failed: verifyField value on the queried row does not match the upsert value";
299
+ upsert?: "Passed" | "Passed (insert only)" | "Not tested" | "Not supported" | "Failed: 'Same as object' not tested" | "Failed: Feature has no test spec" | "Failed: Fatal error during testing" | "Failed: Test spec does not define key field" | "Failed: No test data for insert" | "Failed: No test data for update" | "Failed: No verifyField included with test data" | "Failed: query after insert did not return expected rows" | "Failed: No key path to test update" | "Failed: update returned unexpected row" | "Failed: verifyField value on the upsert row was blank" | "Failed: verifyField value on the queried row does not match the upsert value" | "Failed: Expected values did not match";
244
300
  upsertNotTestedSameAsObjectId?: string;
245
301
  upsertNotTestedReason?: string;
246
302
  upsertFatalError?: CaughtError;
@@ -255,7 +311,15 @@ export interface ObjectTestResult {
255
311
  deleteFatalError?: CaughtError;
256
312
  fieldMetadata?: "Passed" | "Not tested" | "Failed: One or more fields have metadata issues";
257
313
  fieldMetadataIssues: FieldMetadataIssue[];
258
- /** Diagnostic details keyed by feature (list, idsFilter, checkpointFilter, upsert, upsertClean, delete, fieldMetadata). */
314
+ rowFilter?: "Passed" | "Not tested" | "Not supported" | "Failed: Fatal error during testing" | "Failed: Test data invalid, no expected row ids" | "Failed: Row not returned" | "Failed: Returned unexpected row" | "Failed: Expected values did not match" | "Failed: Expected error did not occur";
315
+ rowFilterNotTestedReason?: string;
316
+ rowFilterFatalError?: CaughtError;
317
+ /** Custom checks with this objectId, in declaration order. */
318
+ customChecks?: CustomCheckResult[];
319
+ /**
320
+ * Diagnostic details keyed by feature (list, idsFilter, checkpointFilter, rowFilter, upsert, upsertClean, delete,
321
+ * fieldMetadata) and check:<id>.
322
+ */
259
323
  details?: {
260
324
  [feature: string]: TestDetail[];
261
325
  };
@@ -263,11 +327,11 @@ export interface ObjectTestResult {
263
327
  export interface FieldMetadataIssue {
264
328
  operation: "query" | "upsert" | "delete";
265
329
  path?: string;
266
- issue: "Missing canMatch" | "No test spec for key field" | "Key field from test spec not found on object" | "Key field not tagged with isKey" | "Key field not tagged with isUserKey" | "Key field user key structure incorrect {value: string, operation: string}";
330
+ issue: "Missing canMatch" | "No test spec for key field" | "Key field from test spec not found on object" | "Key field not tagged with isKey" | "Key field not tagged with isUserKey" | "Key field user key structure incorrect {value: string, operation: string}" | "More than one query field tagged with isKey or isUserKey";
267
331
  }
268
332
  export interface MatchRuleTestResult {
269
333
  rule: API.RowMatchRuleType;
270
- status?: "Passed" | "Not tested" | "Failed: Rule has no test spec" | "Failed: Rule not present on object" | "Failed: Fatal error during testing" | "Failed: No test data" | "Failed: Expected match not returned" | "Failed: Wrong match returned" | "Failed: Test data invalid, no expected row ids";
334
+ status?: "Passed" | "Not tested" | "Failed: Rule has no test spec" | "Failed: Rule not present on object" | "Failed: Fatal error during testing" | "Failed: No test data" | "Failed: Expected match not returned" | "Failed: Wrong match returned" | "Failed: Test data invalid, no expected row ids" | "Failed: Expected values did not match" | "Failed: Expected error did not occur";
271
335
  notTestedSameAsObjectId?: string;
272
336
  notTestedReason?: string;
273
337
  errorRowId?: string;
@@ -277,7 +341,7 @@ export interface MatchRuleTestResult {
277
341
  }
278
342
  export interface RelatedFilterTestResult {
279
343
  relationshipId: string;
280
- status?: "Passed" | "Not tested" | "Failed: Relationship has no test spec" | "Failed: Relationship not present on object" | "Failed: Fatal error during testing" | "Failed: No test data" | "Failed: Did not find expected related row" | "Failed: Found unexpected row" | "Failed: Test data invalid, no expected row ids" | "Failed: Test data invalid, 'from' row not returned via idsFilter";
344
+ status?: "Passed" | "Not tested" | "Failed: Relationship has no test spec" | "Failed: Relationship not present on object" | "Failed: Fatal error during testing" | "Failed: No test data" | "Failed: Did not find expected related row" | "Failed: Found unexpected row" | "Failed: Test data invalid, no expected row ids" | "Failed: Test data invalid, 'from' row not returned via idsFilter" | "Failed: Expected values did not match" | "Failed: Expected error did not occur";
281
345
  notTestedSameAsObjectId?: string;
282
346
  notTestedReason?: string;
283
347
  errorRowId?: string;
@@ -328,6 +392,11 @@ export interface UpsertData {
328
392
  verifyFields?: Array<API.JsonValuePath>;
329
393
  /** optionally specify other fields to include when querying the data back for validation */
330
394
  fields?: Array<API.JsonValuePath>;
395
+ /**
396
+ * Assertions on the query-back of this data's row, for values the test did not write (identity and computed columns,
397
+ * defaults, server timestamps, rowversions). Not evaluated by the delete test, which reuses upsertPrepare.
398
+ */
399
+ expect?: UpsertExpect;
331
400
  }
332
401
  /** Expresses how to simulate an upsertClean issue, and defines the expected response. */
333
402
  export interface UpsertIssueSimulator {
@@ -360,6 +429,8 @@ export interface MatchData {
360
429
  expectedMatchRowIds: string[];
361
430
  /** optionally specify the fields to include in the match query */
362
431
  fields?: Array<API.JsonValuePath>;
432
+ /** the match query must fail (error), or its matched rows must hold these values (rows) */
433
+ expect?: Expect;
363
434
  }
364
435
  /** Options passed when requesting data that will be used for a relationship test. */
365
436
  export interface RelationshipOptions {
@@ -384,6 +455,8 @@ export interface RelationshipData {
384
455
  srcFields?: Array<API.JsonValuePath>;
385
456
  /** optionally specify the fields from the 'other' row that should be returned in the response */
386
457
  relatedFields?: Array<API.JsonValuePath>;
458
+ /** the related query must fail (error), or the related rows it returns must hold these values (rows) */
459
+ expect?: Expect;
387
460
  }
388
461
  /** Options passed when requesting data that will be used for a checkpoint test. */
389
462
  export interface CheckpointStep1Options {
@@ -411,12 +484,16 @@ export interface CheckpointStep1Data {
411
484
  expectedRowIds: string[];
412
485
  /** optionally specify the fields to include in the checkpoint query */
413
486
  fields?: Array<API.JsonValuePath>;
487
+ /** the step 1 query must fail (error; step 2 is then skipped), or its rows must hold these values (rows) */
488
+ expect?: Expect;
414
489
  }
415
490
  export interface CheckpointStep2Data {
416
491
  /** id of rows we should receive back in response to the second checkpoint query */
417
492
  expectedRowIds: string[];
418
493
  /** optionally specify the fields to include in the checkpoint query */
419
494
  fields?: Array<API.JsonValuePath>;
495
+ /** the step 2 query must fail (error), or its rows must hold these values (rows) */
496
+ expect?: Expect;
420
497
  }
421
498
  /** Options passed when requesting data that will be used for an list test. */
422
499
  export interface ListOptions {
@@ -435,6 +512,11 @@ export interface ListData {
435
512
  fields?: Array<API.JsonValuePath>;
436
513
  /** if the test harness created test data, place the ids of the rows created here */
437
514
  rowIds?: string[];
515
+ /**
516
+ * The list query must fail (error), or the rows it returns must hold these values (rows). The list reads one page plus
517
+ * one row (pageSize + 1), so a row named by its key must be within that read; use "*" for assertions on every row.
518
+ */
519
+ expect?: Expect;
438
520
  }
439
521
  /** Options passed when requesting data that will be used for an idsFilter test. */
440
522
  export interface IdsFilterOptions {
@@ -451,6 +533,11 @@ export interface IdsFilterData {
451
533
  id2: string;
452
534
  /** optionally specify the fields to include in the idsFilter query */
453
535
  fields?: Array<API.JsonValuePath>;
536
+ /**
537
+ * With error, the connector must reject the ids: the harness runs one query for [id1, id2] and passes only when it
538
+ * fails as described. With rows, the rows that query returns must hold these values.
539
+ */
540
+ expect?: Expect;
454
541
  }
455
542
  /** Options passed when requesting data that will be used for a rowFilters test. */
456
543
  export interface RowFiltersOptions {
@@ -469,7 +556,67 @@ export interface RowFilterData {
469
556
  expectedRowIds: string[];
470
557
  /** optionally specify the fields to include in the rowFilter query */
471
558
  fields?: Array<API.JsonValuePath>;
559
+ /**
560
+ * The row-filter query must fail (error; e.g. a timeout forced with requestTimeoutOverride), or the rows it returns
561
+ * must hold these values (rows). With error set, expectedRowIds may be empty.
562
+ */
563
+ expect?: Expect;
472
564
  }
565
+ /**
566
+ * Expected outcome of a feature's query, beyond which rows come back. The module computes these at prepare time from
567
+ * data it seeded; nothing here should be read from the connector under test.
568
+ */
569
+ export interface Expect {
570
+ /** the operation must fail, and fail like this; a success is a failure of the test */
571
+ error?: ExpectedError;
572
+ /**
573
+ * Assertions on returned rows' values by path, keyed by row meta.key; "*" applies to every returned row, and an
574
+ * assertion under a row's own key overrides a "*" assertion on the same path.
575
+ */
576
+ rows?: Record<string, Assertion[]>;
577
+ }
578
+ /** How an expected failure must look. With neither member set, any error passes. */
579
+ export interface ExpectedError {
580
+ /** must equal the thrown error's `code` (e.g. a ScriptError code) */
581
+ code?: string;
582
+ /** regex source that must match the thrown error's message */
583
+ messagePattern?: string;
584
+ }
585
+ /** Assertions on the query-back of an upsert test row, which cannot be keyed: the insert assigns its key. */
586
+ export interface UpsertExpect {
587
+ /** assertions on the row queried back after the insert */
588
+ insert?: Assertion[];
589
+ /** assertions on the row queried back after the update (not run for insertOnly objects) */
590
+ update?: Assertion[];
591
+ }
592
+ /**
593
+ * One assertion on a value in row.data:
594
+ * - equals: compared per the field's declared type (strings exactly, numbers numerically, booleans, dates as instants at
595
+ * the precision the field's date format implies, objects and arrays structurally; undeclared paths structurally)
596
+ * - matches: regex source the value (as a string) must match, for hex, GUIDs and formats
597
+ * - approx: numeric value within tolerance
598
+ * - present: the value is there and not null
599
+ * - absent: the path is not in the row at all (a column the connector must not echo)
600
+ */
601
+ export type Assertion = {
602
+ path: API.JsonValuePath;
603
+ equals: unknown;
604
+ } | {
605
+ path: API.JsonValuePath;
606
+ matches: string;
607
+ } | {
608
+ path: API.JsonValuePath;
609
+ approx: {
610
+ value: number;
611
+ tolerance: number;
612
+ };
613
+ } | {
614
+ path: API.JsonValuePath;
615
+ present: true;
616
+ } | {
617
+ path: API.JsonValuePath;
618
+ absent: true;
619
+ };
473
620
  /**
474
621
  * Find object field metadata by path.
475
622
  * @param {API.ObjectField[]} fields