@interop/was-conformance-suite 0.1.0
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/LICENSE.md +20 -0
- package/README.md +264 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +69 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/main.d.ts +64 -0
- package/dist/cli/main.d.ts.map +1 -0
- package/dist/cli/main.js +294 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/reporters/json.d.ts +32 -0
- package/dist/cli/reporters/json.d.ts.map +1 -0
- package/dist/cli/reporters/json.js +30 -0
- package/dist/cli/reporters/json.js.map +1 -0
- package/dist/cli/reporters/pretty.d.ts +21 -0
- package/dist/cli/reporters/pretty.d.ts.map +1 -0
- package/dist/cli/reporters/pretty.js +152 -0
- package/dist/cli/reporters/pretty.js.map +1 -0
- package/dist/harness/assert.d.ts +13 -0
- package/dist/harness/assert.d.ts.map +1 -0
- package/dist/harness/assert.js +13 -0
- package/dist/harness/assert.js.map +1 -0
- package/dist/harness/runner.d.ts +25 -0
- package/dist/harness/runner.d.ts.map +1 -0
- package/dist/harness/runner.js +240 -0
- package/dist/harness/runner.js.map +1 -0
- package/dist/harness/types.d.ts +199 -0
- package/dist/harness/types.d.ts.map +1 -0
- package/dist/harness/types.js +2 -0
- package/dist/harness/types.js.map +1 -0
- package/dist/helpers.d.ts +103 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +195 -0
- package/dist/helpers.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/suites/changes-query-api.d.ts +9 -0
- package/dist/suites/changes-query-api.d.ts.map +1 -0
- package/dist/suites/changes-query-api.js +115 -0
- package/dist/suites/changes-query-api.js.map +1 -0
- package/dist/suites/client-backends.d.ts +11 -0
- package/dist/suites/client-backends.d.ts.map +1 -0
- package/dist/suites/client-backends.js +198 -0
- package/dist/suites/client-backends.js.map +1 -0
- package/dist/suites/client-delegation.d.ts +21 -0
- package/dist/suites/client-delegation.d.ts.map +1 -0
- package/dist/suites/client-delegation.js +94 -0
- package/dist/suites/client-delegation.js.map +1 -0
- package/dist/suites/client-export-import.d.ts +19 -0
- package/dist/suites/client-export-import.d.ts.map +1 -0
- package/dist/suites/client-export-import.js +74 -0
- package/dist/suites/client-export-import.js.map +1 -0
- package/dist/suites/client-resources.d.ts +11 -0
- package/dist/suites/client-resources.d.ts.map +1 -0
- package/dist/suites/client-resources.js +193 -0
- package/dist/suites/client-resources.js.map +1 -0
- package/dist/suites/client-spaces.d.ts +11 -0
- package/dist/suites/client-spaces.d.ts.map +1 -0
- package/dist/suites/client-spaces.js +347 -0
- package/dist/suites/client-spaces.js.map +1 -0
- package/dist/suites/collection-api.d.ts +7 -0
- package/dist/suites/collection-api.d.ts.map +1 -0
- package/dist/suites/collection-api.js +266 -0
- package/dist/suites/collection-api.js.map +1 -0
- package/dist/suites/encryption-marker-api.d.ts +9 -0
- package/dist/suites/encryption-marker-api.d.ts.map +1 -0
- package/dist/suites/encryption-marker-api.js +266 -0
- package/dist/suites/encryption-marker-api.js.map +1 -0
- package/dist/suites/index.d.ts +23 -0
- package/dist/suites/index.d.ts.map +1 -0
- package/dist/suites/index.js +32 -0
- package/dist/suites/index.js.map +1 -0
- package/dist/suites/policy-api.d.ts +12 -0
- package/dist/suites/policy-api.d.ts.map +1 -0
- package/dist/suites/policy-api.js +187 -0
- package/dist/suites/policy-api.js.map +1 -0
- package/dist/suites/resource-api.d.ts +8 -0
- package/dist/suites/resource-api.d.ts.map +1 -0
- package/dist/suites/resource-api.js +393 -0
- package/dist/suites/resource-api.js.map +1 -0
- package/dist/suites/server.d.ts +3 -0
- package/dist/suites/server.d.ts.map +1 -0
- package/dist/suites/server.js +25 -0
- package/dist/suites/server.js.map +1 -0
- package/dist/suites/spaces-api.d.ts +11 -0
- package/dist/suites/spaces-api.d.ts.map +1 -0
- package/dist/suites/spaces-api.js +411 -0
- package/dist/suites/spaces-api.js.map +1 -0
- package/package.json +93 -0
package/dist/helpers.js
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
import { ZcapClient } from '@interop/ezcap';
|
|
5
|
+
import { WasClient } from '@interop/was-client';
|
|
6
|
+
import { decodeSecretKeySeed } from '@digitalcredentials/bnid';
|
|
7
|
+
import { Ed25519Signature2020 } from '@interop/ed25519-signature';
|
|
8
|
+
import { Ed25519VerificationKey } from '@interop/ed25519-verification-key';
|
|
9
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
10
|
+
/**
|
|
11
|
+
* Deterministic test-identity seeds. Publishing them is a feature: a server
|
|
12
|
+
* operator can pre-authorize these identities if needed.
|
|
13
|
+
*/
|
|
14
|
+
const secretKeySeeds = {
|
|
15
|
+
alice: 'z1Air2KcEdUpJnJ9m61WFRUFgtC3LHrmGCpwFAkZ7rbbohX',
|
|
16
|
+
aliceDelegatedApp: 'z1AeeM8yN1D3cM56LsPmr3fFKuyv7MC4tdRkeiujMkyRy2u',
|
|
17
|
+
bob: 'z1AmpBeBetWxKMBpAcHsztGogaUki1LXWANSzTd5CiYoikA',
|
|
18
|
+
bobDelegatedApp: 'z1AfgF2HQvQaaAhod3KEYUHwY5epGtP5QmbEMKtMFf8XcYk'
|
|
19
|
+
};
|
|
20
|
+
export function zcapClient({ signer }) {
|
|
21
|
+
return new ZcapClient({
|
|
22
|
+
SuiteClass: Ed25519Signature2020,
|
|
23
|
+
invocationSigner: signer,
|
|
24
|
+
delegationSigner: signer
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Builds a high-level WAS client wrapping a ZcapClient for the given signer.
|
|
29
|
+
* The `serverUrl` is the base for both URL building and zcap invocationTargets.
|
|
30
|
+
*
|
|
31
|
+
* @param options {object}
|
|
32
|
+
* @param options.serverUrl {string}
|
|
33
|
+
* @param options.signer {ISigner}
|
|
34
|
+
* @returns {WasClient}
|
|
35
|
+
*/
|
|
36
|
+
export function wasClient({ serverUrl, signer }) {
|
|
37
|
+
return new WasClient({ serverUrl, zcapClient: zcapClient({ signer }) });
|
|
38
|
+
}
|
|
39
|
+
export async function buildZcapClients({ serverUrl }) {
|
|
40
|
+
const aliceKeyPair = await Ed25519VerificationKey.generate({
|
|
41
|
+
seed: decodeSecretKeySeed({ secretKeySeed: secretKeySeeds.alice })
|
|
42
|
+
});
|
|
43
|
+
const aliceRootDid = `did:key:${aliceKeyPair.fingerprint()}`;
|
|
44
|
+
aliceKeyPair.id = `${aliceRootDid}#${aliceKeyPair.fingerprint()}`;
|
|
45
|
+
const aliceSigner = aliceKeyPair.signer();
|
|
46
|
+
const aliceDelegatedAppKeyPair = await Ed25519VerificationKey.generate({
|
|
47
|
+
seed: decodeSecretKeySeed({
|
|
48
|
+
secretKeySeed: secretKeySeeds.aliceDelegatedApp
|
|
49
|
+
})
|
|
50
|
+
});
|
|
51
|
+
const aliceDelegatedAppDid = `did:key:${aliceDelegatedAppKeyPair.fingerprint()}`;
|
|
52
|
+
aliceDelegatedAppKeyPair.id = `${aliceDelegatedAppDid}#${aliceDelegatedAppKeyPair.fingerprint()}`;
|
|
53
|
+
const bobKeyPair = await Ed25519VerificationKey.generate({
|
|
54
|
+
seed: decodeSecretKeySeed({ secretKeySeed: secretKeySeeds.bob })
|
|
55
|
+
});
|
|
56
|
+
const bobRootDid = `did:key:${bobKeyPair.fingerprint()}`;
|
|
57
|
+
bobKeyPair.id = `${bobRootDid}#${bobKeyPair.fingerprint()}`;
|
|
58
|
+
const bobSigner = bobKeyPair.signer();
|
|
59
|
+
return {
|
|
60
|
+
alice: {
|
|
61
|
+
did: aliceRootDid,
|
|
62
|
+
// Low-level ZcapClient -- kept for raw request()/delegate() calls.
|
|
63
|
+
rootClient: zcapClient({ signer: aliceSigner }),
|
|
64
|
+
// High-level WAS client wrapping the same signer.
|
|
65
|
+
was: wasClient({ serverUrl, signer: aliceSigner })
|
|
66
|
+
},
|
|
67
|
+
aliceDelegatedApp: {
|
|
68
|
+
did: aliceDelegatedAppDid,
|
|
69
|
+
signer: aliceDelegatedAppKeyPair.signer()
|
|
70
|
+
},
|
|
71
|
+
bob: {
|
|
72
|
+
did: bobRootDid,
|
|
73
|
+
rootClient: zcapClient({ signer: bobSigner }),
|
|
74
|
+
was: wasClient({ serverUrl, signer: bobSigner })
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Creates a space on the server using an onboarding token (if configured) or
|
|
80
|
+
* the WAS client. Returns a normalized response object for consistent
|
|
81
|
+
* assertion.
|
|
82
|
+
*
|
|
83
|
+
* The ZCap path goes through `WasClient.request()` -- the client's signed
|
|
84
|
+
* escape hatch -- so the conformance harness exercises the client while still
|
|
85
|
+
* surfacing the raw status/headers/data the create-space assertions rely on.
|
|
86
|
+
*
|
|
87
|
+
* @param options {object}
|
|
88
|
+
* @param options.serverUrl {string}
|
|
89
|
+
* @param options.onboardingToken {string|null}
|
|
90
|
+
* @param options.spaceDescription {object}
|
|
91
|
+
* @param options.rootClient {ZcapClient} ZCap client -- used when no onboarding
|
|
92
|
+
* token is set
|
|
93
|
+
* @returns {Promise<{status: number, headers: Headers, data: any}>}
|
|
94
|
+
*/
|
|
95
|
+
export async function createSpace({ serverUrl, onboardingToken, spaceDescription, rootClient }) {
|
|
96
|
+
if (onboardingToken) {
|
|
97
|
+
const response = await fetch(new URL('/spaces/', serverUrl), {
|
|
98
|
+
method: 'POST',
|
|
99
|
+
headers: {
|
|
100
|
+
'Content-Type': 'application/json',
|
|
101
|
+
Authorization: `Bearer ${onboardingToken}`
|
|
102
|
+
},
|
|
103
|
+
body: JSON.stringify(spaceDescription)
|
|
104
|
+
});
|
|
105
|
+
const data = await response.json();
|
|
106
|
+
return { status: response.status, headers: response.headers, data };
|
|
107
|
+
}
|
|
108
|
+
const was = new WasClient({ serverUrl, zcapClient: rootClient });
|
|
109
|
+
const response = await was.request({
|
|
110
|
+
path: '/spaces/',
|
|
111
|
+
method: 'POST',
|
|
112
|
+
json: spaceDescription
|
|
113
|
+
});
|
|
114
|
+
return {
|
|
115
|
+
status: response.status,
|
|
116
|
+
headers: response.headers,
|
|
117
|
+
data: response.data
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Provisions a Space for the high-level `WasClient` suites: with an onboarding
|
|
122
|
+
* token configured, creates it via the token path (a plain fetch with
|
|
123
|
+
* `Authorization: Bearer`, the same wire form `createSpace` above uses) and
|
|
124
|
+
* returns the client's lazy handle to the new id; otherwise delegates to the
|
|
125
|
+
* client's own signed `createSpace`. This is what lets the client suites run
|
|
126
|
+
* against a server that gates provisioning behind an onboarding token.
|
|
127
|
+
*
|
|
128
|
+
* @param options {object}
|
|
129
|
+
* @param options.serverUrl {string}
|
|
130
|
+
* @param options.onboardingToken {string|null}
|
|
131
|
+
* @param options.was {WasClient} the suite's high-level client
|
|
132
|
+
* @param [options.name] {string} optional Space name
|
|
133
|
+
* @returns {Promise<Space>}
|
|
134
|
+
*/
|
|
135
|
+
export async function provisionSpace({ serverUrl, onboardingToken, was, name }) {
|
|
136
|
+
if (!onboardingToken) {
|
|
137
|
+
return was.createSpace({ name });
|
|
138
|
+
}
|
|
139
|
+
const id = uuidv4();
|
|
140
|
+
const response = await fetch(new URL('/spaces/', serverUrl), {
|
|
141
|
+
method: 'POST',
|
|
142
|
+
headers: {
|
|
143
|
+
'Content-Type': 'application/json',
|
|
144
|
+
Authorization: `Bearer ${onboardingToken}`
|
|
145
|
+
},
|
|
146
|
+
body: JSON.stringify({ id, name, controller: was.controllerDid })
|
|
147
|
+
});
|
|
148
|
+
if (response.status !== 201) {
|
|
149
|
+
throw new Error(`Onboarding-token Create Space failed with status ${response.status}`);
|
|
150
|
+
}
|
|
151
|
+
return was.space(id);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Strips a `createdBy` property from a Space/Collection description, if
|
|
155
|
+
* present, before an exact-shape comparison. The spec makes `createdBy`
|
|
156
|
+
* OPTIONAL ("a client MUST treat an absent `createdBy` as not recorded"), so a
|
|
157
|
+
* conforming external server may legitimately omit it -- and one that records
|
|
158
|
+
* it may report a creator this suite cannot predict. Either way the conformance
|
|
159
|
+
* suite must not assert on it.
|
|
160
|
+
*
|
|
161
|
+
* @param value {unknown}
|
|
162
|
+
* @returns {unknown}
|
|
163
|
+
*/
|
|
164
|
+
export function withoutCreatedBy(value) {
|
|
165
|
+
if (value && typeof value === 'object' && 'createdBy' in value) {
|
|
166
|
+
const { createdBy: _createdBy, ...rest } = value;
|
|
167
|
+
return rest;
|
|
168
|
+
}
|
|
169
|
+
return value;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Builds the per-run conformance context: the deterministic test identities
|
|
173
|
+
* plus the provisioning/utility helpers bound to the target server.
|
|
174
|
+
*
|
|
175
|
+
* @param options {object}
|
|
176
|
+
* @param options.serverUrl {string}
|
|
177
|
+
* @param [options.onboardingToken] {string|null}
|
|
178
|
+
* @returns {Promise<ConformanceContext>}
|
|
179
|
+
*/
|
|
180
|
+
export async function createContext({ serverUrl, onboardingToken = null }) {
|
|
181
|
+
const actors = await buildZcapClients({ serverUrl });
|
|
182
|
+
return {
|
|
183
|
+
serverUrl,
|
|
184
|
+
onboardingToken,
|
|
185
|
+
actors,
|
|
186
|
+
createSpace: ({ spaceDescription, rootClient }) => createSpace({ serverUrl, onboardingToken, spaceDescription, rootClient }),
|
|
187
|
+
provisionSpace: ({ was, name }) => provisionSpace({ serverUrl, onboardingToken, was, name }),
|
|
188
|
+
wasClient: ({ signer }) => wasClient({ serverUrl, signer }),
|
|
189
|
+
zcapClient,
|
|
190
|
+
generateId: uuidv4,
|
|
191
|
+
withoutCreatedBy
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
export { uuidv4 as generateId };
|
|
195
|
+
//# sourceMappingURL=helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { RunEvent, RunReport, Suite } from './harness/types.js';
|
|
2
|
+
export * from './harness/types.js';
|
|
3
|
+
export { runSuites, DEFAULT_TEST_TIMEOUT_MS } from './harness/runner.js';
|
|
4
|
+
export { default as assert } from './harness/assert.js';
|
|
5
|
+
export { buildZcapClients, createContext, createSpace, generateId, provisionSpace, wasClient, withoutCreatedBy, zcapClient } from './helpers.js';
|
|
6
|
+
export * from './suites/index.js';
|
|
7
|
+
/**
|
|
8
|
+
* Runs the conformance suite (or a subset) against a WAS server.
|
|
9
|
+
*
|
|
10
|
+
* @param options {object}
|
|
11
|
+
* @param options.serverUrl {string} base URL of the server under test
|
|
12
|
+
* @param [options.onboardingToken] {string|null} provisioning token, if the
|
|
13
|
+
* server gates space creation
|
|
14
|
+
* @param [options.suites] {Suite[]} subset to run (default: full registry)
|
|
15
|
+
* @param [options.onEvent] {Function} live progress-event callback
|
|
16
|
+
* @param [options.testTimeoutMs] {number} per-test timeout
|
|
17
|
+
* @param [options.failFast] {boolean} stop on first non-optional failure
|
|
18
|
+
* @returns {Promise<RunReport>}
|
|
19
|
+
*/
|
|
20
|
+
export declare function runConformance({ serverUrl, onboardingToken, suites: selectedSuites, onEvent, testTimeoutMs, failFast }: {
|
|
21
|
+
serverUrl: string;
|
|
22
|
+
onboardingToken?: string | null;
|
|
23
|
+
suites?: Array<Suite<any>>;
|
|
24
|
+
onEvent?: (event: RunEvent) => void;
|
|
25
|
+
testTimeoutMs?: number;
|
|
26
|
+
failFast?: boolean;
|
|
27
|
+
}): Promise<RunReport>;
|
|
28
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAEpE,cAAc,oBAAoB,CAAA;AAClC,OAAO,EAAE,SAAS,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAA;AACxE,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,UAAU,EACV,cAAc,EACd,SAAS,EACT,gBAAgB,EAChB,UAAU,EACX,MAAM,cAAc,CAAA;AACrB,cAAc,mBAAmB,CAAA;AAEjC;;;;;;;;;;;;GAYG;AACH,wBAAsB,cAAc,CAAC,EACnC,SAAS,EACT,eAAsB,EACtB,MAAM,EAAE,cAAuB,EAC/B,OAAO,EACP,aAAa,EACb,QAAQ,EACT,EAAE;IACD,SAAS,EAAE,MAAM,CAAA;IACjB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,MAAM,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAA;IAC1B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAA;IACnC,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,GAAG,OAAO,CAAC,SAAS,CAAC,CASrB"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
import { runSuites } from './harness/runner.js';
|
|
5
|
+
import { createContext } from './helpers.js';
|
|
6
|
+
import { suites } from './suites/index.js';
|
|
7
|
+
export * from './harness/types.js';
|
|
8
|
+
export { runSuites, DEFAULT_TEST_TIMEOUT_MS } from './harness/runner.js';
|
|
9
|
+
export { default as assert } from './harness/assert.js';
|
|
10
|
+
export { buildZcapClients, createContext, createSpace, generateId, provisionSpace, wasClient, withoutCreatedBy, zcapClient } from './helpers.js';
|
|
11
|
+
export * from './suites/index.js';
|
|
12
|
+
/**
|
|
13
|
+
* Runs the conformance suite (or a subset) against a WAS server.
|
|
14
|
+
*
|
|
15
|
+
* @param options {object}
|
|
16
|
+
* @param options.serverUrl {string} base URL of the server under test
|
|
17
|
+
* @param [options.onboardingToken] {string|null} provisioning token, if the
|
|
18
|
+
* server gates space creation
|
|
19
|
+
* @param [options.suites] {Suite[]} subset to run (default: full registry)
|
|
20
|
+
* @param [options.onEvent] {Function} live progress-event callback
|
|
21
|
+
* @param [options.testTimeoutMs] {number} per-test timeout
|
|
22
|
+
* @param [options.failFast] {boolean} stop on first non-optional failure
|
|
23
|
+
* @returns {Promise<RunReport>}
|
|
24
|
+
*/
|
|
25
|
+
export async function runConformance({ serverUrl, onboardingToken = null, suites: selectedSuites = suites, onEvent, testTimeoutMs, failFast }) {
|
|
26
|
+
const ctx = await createContext({ serverUrl, onboardingToken });
|
|
27
|
+
return runSuites({
|
|
28
|
+
suites: selectedSuites,
|
|
29
|
+
ctx,
|
|
30
|
+
onEvent,
|
|
31
|
+
testTimeoutMs,
|
|
32
|
+
failFast
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAG1C,cAAc,oBAAoB,CAAA;AAClC,OAAO,EAAE,SAAS,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAA;AACxE,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,UAAU,EACV,cAAc,EACd,SAAS,EACT,gBAAgB,EAChB,UAAU,EACX,MAAM,cAAc,CAAA;AACrB,cAAc,mBAAmB,CAAA;AAEjC;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,EACnC,SAAS,EACT,eAAe,GAAG,IAAI,EACtB,MAAM,EAAE,cAAc,GAAG,MAAM,EAC/B,OAAO,EACP,aAAa,EACb,QAAQ,EAQT;IACC,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC,CAAA;IAC/D,OAAO,SAAS,CAAC;QACf,MAAM,EAAE,cAAc;QACtB,GAAG;QACH,OAAO;QACP,aAAa;QACb,QAAQ;KACT,CAAC,CAAA;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changes-query-api.d.ts","sourceRoot":"","sources":["../../src/suites/changes-query-api.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAEhD,UAAU,KAAK;IACb,KAAK,EAAE,GAAG,CAAA;IACV,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,MAAM,CAAA;CACvB;AAED,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,KAAK,CA8HxC,CAAA"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* WAS conformance tests -- Collection `changes` query profile (the replication
|
|
6
|
+
* change feed served at `POST /space/:s/:c/query`; spec "Collection-level
|
|
7
|
+
* reserved endpoints").
|
|
8
|
+
*/
|
|
9
|
+
import assert from '../harness/assert.js';
|
|
10
|
+
export const changesQueryApi = {
|
|
11
|
+
id: 'changes-query-api',
|
|
12
|
+
name: 'Collection changes query profile',
|
|
13
|
+
setup: async (ctx) => {
|
|
14
|
+
const alice = { ...ctx.actors.alice };
|
|
15
|
+
const collectionId = 'feed';
|
|
16
|
+
/** Absolute URL for this Space's `feed` collection query endpoint. */
|
|
17
|
+
function queryUrl() {
|
|
18
|
+
return new URL(`/space/${alice.space1.id}/${collectionId}/query`, ctx.serverUrl).toString();
|
|
19
|
+
}
|
|
20
|
+
alice.space1 = { id: ctx.generateId() };
|
|
21
|
+
await ctx.createSpace({
|
|
22
|
+
spaceDescription: {
|
|
23
|
+
id: alice.space1.id,
|
|
24
|
+
name: "Alice's Space #1",
|
|
25
|
+
controller: alice.did
|
|
26
|
+
},
|
|
27
|
+
rootClient: alice.rootClient
|
|
28
|
+
});
|
|
29
|
+
await alice.rootClient.request({
|
|
30
|
+
url: new URL(`/space/${alice.space1.id}/`, ctx.serverUrl).toString(),
|
|
31
|
+
method: 'POST',
|
|
32
|
+
action: 'POST',
|
|
33
|
+
json: { id: collectionId, name: 'Feed' }
|
|
34
|
+
});
|
|
35
|
+
// Three JSON documents by id; the middle one is then soft-deleted.
|
|
36
|
+
for (const id of ['r1', 'r2', 'r3']) {
|
|
37
|
+
await alice.rootClient.request({
|
|
38
|
+
url: new URL(`/space/${alice.space1.id}/${collectionId}/${id}`, ctx.serverUrl).toString(),
|
|
39
|
+
method: 'PUT',
|
|
40
|
+
action: 'PUT',
|
|
41
|
+
json: { id }
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
await alice.rootClient.request({
|
|
45
|
+
url: new URL(`/space/${alice.space1.id}/${collectionId}/r2`, ctx.serverUrl).toString(),
|
|
46
|
+
method: 'DELETE'
|
|
47
|
+
});
|
|
48
|
+
return { alice, collectionId, queryUrl };
|
|
49
|
+
},
|
|
50
|
+
teardown: async (ctx, state) => {
|
|
51
|
+
const { alice } = state;
|
|
52
|
+
try {
|
|
53
|
+
await alice.rootClient.request({
|
|
54
|
+
url: new URL(`/space/${alice.space1.id}`, ctx.serverUrl).toString(),
|
|
55
|
+
method: 'DELETE'
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
/* best-effort cleanup */
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
tests: [
|
|
63
|
+
{
|
|
64
|
+
id: 'changes.live-docs-tombstone-checkpoint',
|
|
65
|
+
name: '[root] returns live documents and a tombstone, with a checkpoint',
|
|
66
|
+
run: async (ctx, state) => {
|
|
67
|
+
const { alice, queryUrl } = state;
|
|
68
|
+
const response = await alice.rootClient.request({
|
|
69
|
+
url: queryUrl(),
|
|
70
|
+
method: 'POST',
|
|
71
|
+
action: 'POST',
|
|
72
|
+
json: { profile: 'changes', limit: 100 }
|
|
73
|
+
});
|
|
74
|
+
assert.equal(response.status, 200);
|
|
75
|
+
assert.match(response.headers.get('content-type'), /application\/json/);
|
|
76
|
+
const byId = new Map(response.data.documents.map((doc) => [doc.id, doc]));
|
|
77
|
+
assert.deepEqual([...byId.keys()].sort(), ['r1', 'r2', 'r3']);
|
|
78
|
+
// Live documents carry their body under `data` and `_deleted: false`.
|
|
79
|
+
assert.equal(byId.get('r1')._deleted, false);
|
|
80
|
+
assert.deepEqual(byId.get('r1').data, { id: 'r1' });
|
|
81
|
+
// The deleted document is a tombstone: `_deleted: true`, no `data`.
|
|
82
|
+
const tombstone = byId.get('r2');
|
|
83
|
+
assert.equal(tombstone._deleted, true);
|
|
84
|
+
assert.equal(tombstone.data, undefined);
|
|
85
|
+
// The checkpoint is the last returned document's keyset position.
|
|
86
|
+
assert.ok(response.data.checkpoint);
|
|
87
|
+
assert.equal(typeof response.data.checkpoint.id, 'string');
|
|
88
|
+
assert.equal(typeof response.data.checkpoint.updatedAt, 'string');
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: 'changes.unknown-profile-501',
|
|
93
|
+
name: '[root] rejects an unknown query profile with 501',
|
|
94
|
+
run: async (ctx, state) => {
|
|
95
|
+
const { alice, queryUrl } = state;
|
|
96
|
+
let thrown;
|
|
97
|
+
try {
|
|
98
|
+
await alice.rootClient.request({
|
|
99
|
+
url: queryUrl(),
|
|
100
|
+
method: 'POST',
|
|
101
|
+
action: 'POST',
|
|
102
|
+
json: { profile: 'no-such-profile' }
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
thrown = err;
|
|
107
|
+
}
|
|
108
|
+
assert.ok(thrown, 'expected an unknown profile to be rejected');
|
|
109
|
+
assert.equal(thrown.response.status, 501);
|
|
110
|
+
assert.match(thrown.response.headers.get('content-type'), /application\/problem\+json/);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
]
|
|
114
|
+
};
|
|
115
|
+
//# sourceMappingURL=changes-query-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changes-query-api.js","sourceRoot":"","sources":["../../src/suites/changes-query-api.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;GAIG;AACH,OAAO,MAAM,MAAM,sBAAsB,CAAA;AASzC,MAAM,CAAC,MAAM,eAAe,GAAiB;IAC3C,EAAE,EAAE,mBAAmB;IACvB,IAAI,EAAE,kCAAkC;IAExC,KAAK,EAAE,KAAK,EAAC,GAAG,EAAC,EAAE;QACjB,MAAM,KAAK,GAAQ,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,CAAA;QAC1C,MAAM,YAAY,GAAG,MAAM,CAAA;QAE3B,sEAAsE;QACtE,SAAS,QAAQ;YACf,OAAO,IAAI,GAAG,CACZ,UAAU,KAAK,CAAC,MAAM,CAAC,EAAE,IAAI,YAAY,QAAQ,EACjD,GAAG,CAAC,SAAS,CACd,CAAC,QAAQ,EAAE,CAAA;QACd,CAAC;QAED,KAAK,CAAC,MAAM,GAAG,EAAE,EAAE,EAAE,GAAG,CAAC,UAAU,EAAE,EAAE,CAAA;QACvC,MAAM,GAAG,CAAC,WAAW,CAAC;YACpB,gBAAgB,EAAE;gBAChB,EAAE,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE;gBACnB,IAAI,EAAE,kBAAkB;gBACxB,UAAU,EAAE,KAAK,CAAC,GAAG;aACtB;YACD,UAAU,EAAE,KAAK,CAAC,UAAU;SAC7B,CAAC,CAAA;QACF,MAAM,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC;YAC7B,GAAG,EAAE,IAAI,GAAG,CAAC,UAAU,KAAK,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,QAAQ,EAAE;YACpE,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE;SACzC,CAAC,CAAA;QACF,mEAAmE;QACnE,KAAK,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;YACpC,MAAM,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC;gBAC7B,GAAG,EAAE,IAAI,GAAG,CACV,UAAU,KAAK,CAAC,MAAM,CAAC,EAAE,IAAI,YAAY,IAAI,EAAE,EAAE,EACjD,GAAG,CAAC,SAAS,CACd,CAAC,QAAQ,EAAE;gBACZ,MAAM,EAAE,KAAK;gBACb,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,EAAE,EAAE,EAAE;aACb,CAAC,CAAA;QACJ,CAAC;QACD,MAAM,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC;YAC7B,GAAG,EAAE,IAAI,GAAG,CACV,UAAU,KAAK,CAAC,MAAM,CAAC,EAAE,IAAI,YAAY,KAAK,EAC9C,GAAG,CAAC,SAAS,CACd,CAAC,QAAQ,EAAE;YACZ,MAAM,EAAE,QAAQ;SACjB,CAAC,CAAA;QAEF,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAA;IAC1C,CAAC;IAED,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE;QAC7B,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,CAAA;QACvB,IAAI,CAAC;YACH,MAAM,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC;gBAC7B,GAAG,EAAE,IAAI,GAAG,CAAC,UAAU,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,QAAQ,EAAE;gBACnE,MAAM,EAAE,QAAQ;aACjB,CAAC,CAAA;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,yBAAyB;QAC3B,CAAC;IACH,CAAC;IAED,KAAK,EAAE;QACL;YACE,EAAE,EAAE,wCAAwC;YAC5C,IAAI,EAAE,kEAAkE;YACxE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE;gBACxB,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAA;gBACjC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC;oBAC9C,GAAG,EAAE,QAAQ,EAAE;oBACf,MAAM,EAAE,MAAM;oBACd,MAAM,EAAE,MAAM;oBACd,IAAI,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE;iBACzC,CAAC,CAAA;gBACF,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;gBAClC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,mBAAmB,CAAC,CAAA;gBAEvE,MAAM,IAAI,GAAG,IAAI,GAAG,CAClB,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAQ,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CACzD,CAAA;gBACD,MAAM,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAA;gBAE7D,sEAAsE;gBACtE,MAAM,CAAC,KAAK,CAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAS,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;gBACrD,MAAM,CAAC,SAAS,CAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAS,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;gBAE5D,oEAAoE;gBACpE,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAQ,CAAA;gBACvC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;gBACtC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;gBAEvC,kEAAkE;gBAClE,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;gBACnC,MAAM,CAAC,KAAK,CAAC,OAAO,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAA;gBAC1D,MAAM,CAAC,KAAK,CAAC,OAAO,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAA;YACnE,CAAC;SACF;QACD;YACE,EAAE,EAAE,6BAA6B;YACjC,IAAI,EAAE,kDAAkD;YACxD,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE;gBACxB,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAA;gBACjC,IAAI,MAAW,CAAA;gBACf,IAAI,CAAC;oBACH,MAAM,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC;wBAC7B,GAAG,EAAE,QAAQ,EAAE;wBACf,MAAM,EAAE,MAAM;wBACd,MAAM,EAAE,MAAM;wBACd,IAAI,EAAE,EAAE,OAAO,EAAE,iBAAiB,EAAE;qBACrC,CAAC,CAAA;gBACJ,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,MAAM,GAAG,GAAG,CAAA;gBACd,CAAC;gBACD,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,4CAA4C,CAAC,CAAA;gBAC/D,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;gBACzC,MAAM,CAAC,KAAK,CACV,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,EAC3C,4BAA4B,CAC7B,CAAA;YACH,CAAC;SACF;KACF;CACF,CAAA"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Suite } from '../harness/types.js';
|
|
2
|
+
import type { Space, BackendRegistration } from '@interop/was-client';
|
|
3
|
+
interface State {
|
|
4
|
+
alice: any;
|
|
5
|
+
createdSpaces: Space[];
|
|
6
|
+
newSpace: (name: string) => Promise<Space>;
|
|
7
|
+
gdriveRegistration: (id: string) => BackendRegistration;
|
|
8
|
+
}
|
|
9
|
+
export declare const clientBackends: Suite<State>;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=client-backends.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-backends.d.ts","sourceRoot":"","sources":["../../src/suites/client-backends.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAGhD,OAAO,KAAK,EAAE,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAErE,UAAU,KAAK;IACb,KAAK,EAAE,GAAG,CAAA;IACV,aAAa,EAAE,KAAK,EAAE,CAAA;IACtB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,CAAC,CAAA;IAC1C,kBAAkB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,mBAAmB,CAAA;CACxD;AAED,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,KAAK,CA+MvC,CAAA"}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* WAS conformance tests -- high-level WasClient: BYOS backend registration.
|
|
6
|
+
*
|
|
7
|
+
* Drives the published `@interop/was-client` backend-registration control plane
|
|
8
|
+
* (`space.registerBackend` / `updateBackend` / `deregisterBackend`, and
|
|
9
|
+
* selecting a registered backend on a Collection) against a live server, the
|
|
10
|
+
* write side of the spec's "Backends" section. The reference server registers no
|
|
11
|
+
* provider adapters, so a registered `external` backend is selectable but its
|
|
12
|
+
* data plane is inert -- the suite asserts that "registered but not operable"
|
|
13
|
+
* contract too.
|
|
14
|
+
*/
|
|
15
|
+
import assert from '../harness/assert.js';
|
|
16
|
+
import { ConflictError, ValidationError } from '@interop/was-client';
|
|
17
|
+
export const clientBackends = {
|
|
18
|
+
id: 'client-backends',
|
|
19
|
+
name: 'WasClient — BYOS backend registration',
|
|
20
|
+
setup: async (ctx) => {
|
|
21
|
+
// Shallow-clone the shared actor so per-suite scratch fields do not leak
|
|
22
|
+
// into other suites.
|
|
23
|
+
const alice = { ...ctx.actors.alice };
|
|
24
|
+
const createdSpaces = [];
|
|
25
|
+
// Creates a space via the high-level client and registers it for teardown.
|
|
26
|
+
const newSpace = async (name) => {
|
|
27
|
+
const space = await ctx.provisionSpace({ was: alice.was, name });
|
|
28
|
+
createdSpaces.push(space);
|
|
29
|
+
return space;
|
|
30
|
+
};
|
|
31
|
+
// A representative registration body. `connection` is deliberately
|
|
32
|
+
// secret-bearing (an OAuth authorization code) so the suite can assert the
|
|
33
|
+
// server never echoes it back on the sanitized read.
|
|
34
|
+
const gdriveRegistration = (id) => {
|
|
35
|
+
return {
|
|
36
|
+
id,
|
|
37
|
+
name: 'My Google Drive',
|
|
38
|
+
provider: 'google-drive',
|
|
39
|
+
storageMode: ['document', 'blob'],
|
|
40
|
+
connection: {
|
|
41
|
+
kind: 'oauth2-google',
|
|
42
|
+
authorizationCode: 'secret-auth-code',
|
|
43
|
+
scope: 'https://www.googleapis.com/auth/drive.file',
|
|
44
|
+
rootFolderName: 'WAS'
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
return { alice, createdSpaces, newSpace, gdriveRegistration };
|
|
49
|
+
},
|
|
50
|
+
teardown: async (ctx, state) => {
|
|
51
|
+
for (const space of state.createdSpaces) {
|
|
52
|
+
try {
|
|
53
|
+
await space.delete();
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
/* best-effort cleanup */
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
tests: [
|
|
61
|
+
{
|
|
62
|
+
id: 'backend.register-sanitized',
|
|
63
|
+
name: 'registers a backend and returns a sanitized (secret-free) descriptor',
|
|
64
|
+
run: async (ctx, state) => {
|
|
65
|
+
const { newSpace, gdriveRegistration } = state;
|
|
66
|
+
const space = await newSpace('Register Backend');
|
|
67
|
+
const descriptor = await space.registerBackend(gdriveRegistration('gdrive-personal'));
|
|
68
|
+
assert.equal(descriptor.id, 'gdrive-personal');
|
|
69
|
+
assert.equal(descriptor.name, 'My Google Drive');
|
|
70
|
+
assert.equal(descriptor.managedBy, 'external');
|
|
71
|
+
assert.equal(descriptor.provider, 'google-drive');
|
|
72
|
+
assert.deepStrictEqual(descriptor.storageMode, ['document', 'blob']);
|
|
73
|
+
// The connection is sanitized: public fields surface (and `status` starts at
|
|
74
|
+
// `registered`, since no provider adapter has connected it yet).
|
|
75
|
+
const connection = descriptor.connection;
|
|
76
|
+
assert.ok(connection);
|
|
77
|
+
assert.equal(connection.kind, 'oauth2-google');
|
|
78
|
+
assert.equal(connection.status, 'registered');
|
|
79
|
+
assert.equal(connection.scope, 'https://www.googleapis.com/auth/drive.file');
|
|
80
|
+
assert.equal(connection.rootFolderName, 'WAS');
|
|
81
|
+
assert.match(connection.connectedAt ?? '', /^\d{4}-\d{2}-\d{2}T/);
|
|
82
|
+
// The secret-bearing authorization code must never be echoed back anywhere.
|
|
83
|
+
assert.ok(!JSON.stringify(descriptor).includes('secret-auth-code'), 'the sanitized descriptor must not leak the authorization code');
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
id: 'backend.list-with-default',
|
|
88
|
+
name: 'lists the registered backend alongside the server default',
|
|
89
|
+
run: async (ctx, state) => {
|
|
90
|
+
const { newSpace, gdriveRegistration } = state;
|
|
91
|
+
const space = await newSpace('List Backends');
|
|
92
|
+
await space.registerBackend(gdriveRegistration('gdrive-personal'));
|
|
93
|
+
const backends = await space.backends();
|
|
94
|
+
assert.ok(backends);
|
|
95
|
+
assert.equal(backends.length, 2);
|
|
96
|
+
assert.ok(backends.some(backend => backend.id === 'default'));
|
|
97
|
+
const gdrive = backends.find(backend => backend.id === 'gdrive-personal');
|
|
98
|
+
assert.ok(gdrive);
|
|
99
|
+
assert.equal(gdrive.provider, 'google-drive');
|
|
100
|
+
assert.equal(gdrive.connection?.status, 'registered');
|
|
101
|
+
// No secret leaks on the list path either.
|
|
102
|
+
assert.ok(!JSON.stringify(gdrive).includes('secret-auth-code'));
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: 'backend.duplicate-id-conflict',
|
|
107
|
+
name: 'rejects a duplicate backend id with ConflictError',
|
|
108
|
+
run: async (ctx, state) => {
|
|
109
|
+
const { newSpace, gdriveRegistration } = state;
|
|
110
|
+
const space = await newSpace('Duplicate Backend');
|
|
111
|
+
await space.registerBackend(gdriveRegistration('gdrive-personal'));
|
|
112
|
+
await assert.rejects(space.registerBackend(gdriveRegistration('gdrive-personal')), (err) => err instanceof ConflictError);
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
id: 'backend.reserved-default-id-validation',
|
|
117
|
+
name: 'rejects registering the reserved "default" id with ValidationError',
|
|
118
|
+
run: async (ctx, state) => {
|
|
119
|
+
const { newSpace, gdriveRegistration } = state;
|
|
120
|
+
const space = await newSpace('Reserved Backend Id');
|
|
121
|
+
await assert.rejects(space.registerBackend(gdriveRegistration('default')), (err) => err instanceof ValidationError);
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
id: 'backend.update-create-then-replace',
|
|
126
|
+
name: 'updateBackend creates a record (descriptor) then replaces it in place (null)',
|
|
127
|
+
run: async (ctx, state) => {
|
|
128
|
+
const { newSpace, gdriveRegistration } = state;
|
|
129
|
+
const space = await newSpace('Update Backend');
|
|
130
|
+
// PUT to a fresh id creates the record -> 201 + sanitized descriptor.
|
|
131
|
+
const created = await space.updateBackend(gdriveRegistration('gdrive-personal'));
|
|
132
|
+
assert.ok(created);
|
|
133
|
+
assert.equal(created.id, 'gdrive-personal');
|
|
134
|
+
assert.equal(created.connection?.status, 'registered');
|
|
135
|
+
// PUT to the same id replaces it in place -> 204, no body -> null.
|
|
136
|
+
const replaced = await space.updateBackend({
|
|
137
|
+
id: 'gdrive-personal',
|
|
138
|
+
provider: 'google-drive',
|
|
139
|
+
connection: { kind: 'oauth2-google', authorizationCode: 'fresh-code' }
|
|
140
|
+
});
|
|
141
|
+
assert.equal(replaced, null);
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
id: 'backend.select-on-collection',
|
|
146
|
+
name: 'selects a registered backend on a Collection (control plane)',
|
|
147
|
+
run: async (ctx, state) => {
|
|
148
|
+
const { newSpace, gdriveRegistration } = state;
|
|
149
|
+
const space = await newSpace('Select Backend');
|
|
150
|
+
await space.registerBackend(gdriveRegistration('gdrive-personal'));
|
|
151
|
+
const collection = await space.createCollection({
|
|
152
|
+
id: 'on-gdrive',
|
|
153
|
+
backend: { id: 'gdrive-personal' }
|
|
154
|
+
});
|
|
155
|
+
const description = await collection.describe();
|
|
156
|
+
assert.deepStrictEqual(description?.backend, { id: 'gdrive-personal' });
|
|
157
|
+
// "Collection Backend Selected" resolves to the registered descriptor.
|
|
158
|
+
const backend = await collection.backend();
|
|
159
|
+
assert.equal(backend?.id, 'gdrive-personal');
|
|
160
|
+
assert.equal(backend?.provider, 'google-drive');
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
id: 'backend.inert-data-plane',
|
|
165
|
+
name: 'a registered backend with no provider adapter is inert (data plane fails closed)',
|
|
166
|
+
optional: true,
|
|
167
|
+
run: async (ctx, state) => {
|
|
168
|
+
const { newSpace, gdriveRegistration } = state;
|
|
169
|
+
const space = await newSpace('Inert Backend');
|
|
170
|
+
await space.registerBackend(gdriveRegistration('gdrive-personal'));
|
|
171
|
+
const collection = await space.createCollection({
|
|
172
|
+
id: 'on-gdrive',
|
|
173
|
+
backend: { id: 'gdrive-personal' }
|
|
174
|
+
});
|
|
175
|
+
// The reference server registers no provider adapters, so writing a resource
|
|
176
|
+
// to the selected backend fails closed with `unsupported-backend` (409).
|
|
177
|
+
await assert.rejects(collection.add({ hello: 'world' }), (err) => err instanceof ConflictError &&
|
|
178
|
+
(err.type ?? '').includes('unsupported-backend'));
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
id: 'backend.deregister-idempotent',
|
|
183
|
+
name: 'deregisters a backend and is idempotent',
|
|
184
|
+
run: async (ctx, state) => {
|
|
185
|
+
const { newSpace, gdriveRegistration } = state;
|
|
186
|
+
const space = await newSpace('Deregister Backend');
|
|
187
|
+
await space.registerBackend(gdriveRegistration('gdrive-personal'));
|
|
188
|
+
await space.deregisterBackend('gdrive-personal');
|
|
189
|
+
const backends = await space.backends();
|
|
190
|
+
assert.ok(backends);
|
|
191
|
+
assert.ok(!backends.some(backend => backend.id === 'gdrive-personal'));
|
|
192
|
+
// Deregistering again must not throw (idempotent).
|
|
193
|
+
await space.deregisterBackend('gdrive-personal');
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
]
|
|
197
|
+
};
|
|
198
|
+
//# sourceMappingURL=client-backends.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-backends.js","sourceRoot":"","sources":["../../src/suites/client-backends.ts"],"names":[],"mappings":"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"}
|