@kya-os/mcp-i-core 1.2.2-canary.25 → 1.2.2-canary.27
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/.claude/settings.local.json +9 -0
- package/.turbo/turbo-build.log +4 -0
- package/.turbo/turbo-test$colon$coverage.log +28 -0
- package/.turbo/turbo-test.log +2398 -0
- package/COMPLIANCE_IMPROVEMENT_REPORT.md +483 -0
- package/Composer 3.md +615 -0
- package/GPT-5.md +1169 -0
- package/OPUS-plan.md +352 -0
- package/PHASE_3_AND_4.1_SUMMARY.md +585 -0
- package/PHASE_3_SUMMARY.md +317 -0
- package/PHASE_4.1.3_SUMMARY.md +428 -0
- package/PHASE_4.1_COMPLETE.md +525 -0
- package/PHASE_4_USER_DID_IDENTITY_LINKING_PLAN.md +1240 -0
- package/SCHEMA_COMPLIANCE_REPORT.md +275 -0
- package/TEST_PLAN.md +571 -0
- package/dist/__tests__/utils/mock-providers.d.ts +1 -2
- package/dist/__tests__/utils/mock-providers.d.ts.map +1 -1
- package/dist/__tests__/utils/mock-providers.js.map +1 -1
- package/dist/services/authorization/authorization-registry.d.ts +29 -0
- package/dist/services/authorization/authorization-registry.d.ts.map +1 -0
- package/dist/services/authorization/authorization-registry.js +57 -0
- package/dist/services/authorization/authorization-registry.js.map +1 -0
- package/dist/services/authorization/types.d.ts +53 -0
- package/dist/services/authorization/types.d.ts.map +1 -0
- package/dist/services/authorization/types.js +10 -0
- package/dist/services/authorization/types.js.map +1 -0
- package/docs/API_REFERENCE.md +1362 -0
- package/docs/COMPLIANCE_MATRIX.md +691 -0
- package/docs/STATUSLIST2021_GUIDE.md +696 -0
- package/docs/W3C_VC_DELEGATION_GUIDE.md +710 -0
- package/package.json +21 -63
- package/scripts/audit-compliance.ts +724 -0
- package/src/__tests__/cache/tool-protection-cache.test.ts +640 -0
- package/src/__tests__/config/provider-runtime-config.test.ts +309 -0
- package/src/__tests__/delegation-e2e.test.ts +690 -0
- package/src/__tests__/identity/user-did-manager.test.ts +213 -0
- package/src/__tests__/index.test.ts +56 -0
- package/src/__tests__/integration/full-flow.test.ts +776 -0
- package/src/__tests__/integration.test.ts +281 -0
- package/src/__tests__/providers/base.test.ts +173 -0
- package/src/__tests__/providers/memory.test.ts +319 -0
- package/src/__tests__/regression/phase2-regression.test.ts +427 -0
- package/src/__tests__/runtime/audit-logger.test.ts +154 -0
- package/src/__tests__/runtime/base-extensions.test.ts +593 -0
- package/src/__tests__/runtime/base.test.ts +869 -0
- package/src/__tests__/runtime/delegation-flow.test.ts +164 -0
- package/src/__tests__/runtime/proof-client-did.test.ts +375 -0
- package/src/__tests__/runtime/route-interception.test.ts +686 -0
- package/src/__tests__/runtime/tool-protection-enforcement.test.ts +908 -0
- package/src/__tests__/services/agentshield-integration.test.ts +784 -0
- package/src/__tests__/services/provider-resolver-edge-cases.test.ts +487 -0
- package/src/__tests__/services/tool-protection-oauth-provider.test.ts +480 -0
- package/src/__tests__/services/tool-protection.service.test.ts +1366 -0
- package/src/__tests__/utils/mock-providers.ts +340 -0
- package/src/cache/oauth-config-cache.d.ts +69 -0
- package/src/cache/oauth-config-cache.d.ts.map +1 -0
- package/src/cache/oauth-config-cache.js +71 -0
- package/src/cache/oauth-config-cache.js.map +1 -0
- package/src/cache/oauth-config-cache.ts +123 -0
- package/src/cache/tool-protection-cache.ts +171 -0
- package/src/compliance/EXAMPLE.md +412 -0
- package/src/compliance/__tests__/schema-verifier.test.ts +797 -0
- package/src/compliance/index.ts +8 -0
- package/src/compliance/schema-registry.ts +460 -0
- package/src/compliance/schema-verifier.ts +708 -0
- package/src/config/__tests__/remote-config.spec.ts +268 -0
- package/src/config/remote-config.ts +174 -0
- package/src/config.ts +309 -0
- package/src/delegation/__tests__/audience-validator.test.ts +112 -0
- package/src/delegation/__tests__/bitstring.test.ts +346 -0
- package/src/delegation/__tests__/cascading-revocation.test.ts +628 -0
- package/src/delegation/__tests__/delegation-graph.test.ts +584 -0
- package/src/delegation/__tests__/utils.test.ts +152 -0
- package/src/delegation/__tests__/vc-issuer.test.ts +442 -0
- package/src/delegation/__tests__/vc-verifier.test.ts +922 -0
- package/src/delegation/audience-validator.ts +52 -0
- package/src/delegation/bitstring.ts +278 -0
- package/src/delegation/cascading-revocation.ts +370 -0
- package/src/delegation/delegation-graph.ts +299 -0
- package/src/delegation/index.ts +14 -0
- package/src/delegation/statuslist-manager.ts +353 -0
- package/src/delegation/storage/__tests__/memory-graph-storage.test.ts +366 -0
- package/src/delegation/storage/__tests__/memory-statuslist-storage.test.ts +228 -0
- package/src/delegation/storage/index.ts +9 -0
- package/src/delegation/storage/memory-graph-storage.ts +178 -0
- package/src/delegation/storage/memory-statuslist-storage.ts +77 -0
- package/src/delegation/utils.ts +42 -0
- package/src/delegation/vc-issuer.ts +232 -0
- package/src/delegation/vc-verifier.ts +568 -0
- package/src/identity/idp-token-resolver.ts +147 -0
- package/src/identity/idp-token-storage.interface.ts +59 -0
- package/src/identity/user-did-manager.ts +370 -0
- package/src/index.ts +260 -0
- package/src/providers/base.d.ts +91 -0
- package/src/providers/base.d.ts.map +1 -0
- package/src/providers/base.js +38 -0
- package/src/providers/base.js.map +1 -0
- package/src/providers/base.ts +96 -0
- package/src/providers/memory.ts +142 -0
- package/src/runtime/audit-logger.ts +39 -0
- package/src/runtime/base.ts +1329 -0
- package/src/services/__tests__/access-control.integration.test.ts +443 -0
- package/src/services/__tests__/access-control.service.test.ts +970 -0
- package/src/services/__tests__/batch-delegation.service.test.ts +351 -0
- package/src/services/__tests__/crypto.service.test.ts +531 -0
- package/src/services/__tests__/oauth-provider-registry.test.ts +142 -0
- package/src/services/__tests__/proof-verifier.integration.test.ts +485 -0
- package/src/services/__tests__/proof-verifier.test.ts +489 -0
- package/src/services/__tests__/provider-resolution.integration.test.ts +198 -0
- package/src/services/__tests__/provider-resolver.test.ts +217 -0
- package/src/services/__tests__/storage.service.test.ts +358 -0
- package/src/services/access-control.service.ts +877 -0
- package/src/services/authorization/authorization-registry.ts +66 -0
- package/src/services/authorization/types.ts +71 -0
- package/src/services/batch-delegation.service.ts +137 -0
- package/src/services/crypto.service.ts +302 -0
- package/src/services/errors.ts +76 -0
- package/src/services/index.ts +9 -0
- package/src/services/oauth-config.service.d.ts +53 -0
- package/src/services/oauth-config.service.d.ts.map +1 -0
- package/src/services/oauth-config.service.js +113 -0
- package/src/services/oauth-config.service.js.map +1 -0
- package/src/services/oauth-config.service.ts +166 -0
- package/src/services/oauth-provider-registry.d.ts +57 -0
- package/src/services/oauth-provider-registry.d.ts.map +1 -0
- package/src/services/oauth-provider-registry.js +73 -0
- package/src/services/oauth-provider-registry.js.map +1 -0
- package/src/services/oauth-provider-registry.ts +123 -0
- package/src/services/oauth-service.ts +510 -0
- package/src/services/oauth-token-retrieval.service.ts +245 -0
- package/src/services/proof-verifier.ts +478 -0
- package/src/services/provider-resolver.d.ts +48 -0
- package/src/services/provider-resolver.d.ts.map +1 -0
- package/src/services/provider-resolver.js +106 -0
- package/src/services/provider-resolver.js.map +1 -0
- package/src/services/provider-resolver.ts +144 -0
- package/src/services/provider-validator.ts +170 -0
- package/src/services/storage.service.ts +566 -0
- package/src/services/tool-context-builder.ts +172 -0
- package/src/services/tool-protection.service.ts +798 -0
- package/src/types/oauth-required-error.ts +63 -0
- package/src/types/tool-protection.ts +155 -0
- package/src/utils/__tests__/did-helpers.test.ts +101 -0
- package/src/utils/base64.ts +148 -0
- package/src/utils/cors.ts +83 -0
- package/src/utils/did-helpers.ts +150 -0
- package/src/utils/index.ts +8 -0
- package/src/utils/storage-keys.ts +278 -0
- package/tsconfig.json +21 -0
- package/vitest.config.ts +56 -0
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
DelegationGraphManager,
|
|
4
|
+
createDelegationGraph,
|
|
5
|
+
type DelegationNode,
|
|
6
|
+
type DelegationGraphStorageProvider,
|
|
7
|
+
} from "../delegation-graph.js";
|
|
8
|
+
|
|
9
|
+
describe("DelegationGraphManager", () => {
|
|
10
|
+
let storage: DelegationGraphStorageProvider;
|
|
11
|
+
let graph: DelegationGraphManager;
|
|
12
|
+
|
|
13
|
+
// Simple in-memory storage for testing
|
|
14
|
+
const createMockStorage = (): DelegationGraphStorageProvider => {
|
|
15
|
+
const nodes = new Map<string, DelegationNode>();
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
getNode: vi.fn(async (id: string) => {
|
|
19
|
+
return nodes.get(id) || null;
|
|
20
|
+
}),
|
|
21
|
+
setNode: vi.fn(async (node: DelegationNode) => {
|
|
22
|
+
nodes.set(node.id, { ...node });
|
|
23
|
+
}),
|
|
24
|
+
getChildren: vi.fn(async (id: string) => {
|
|
25
|
+
const node = nodes.get(id);
|
|
26
|
+
if (!node) return [];
|
|
27
|
+
return node.children
|
|
28
|
+
.map((childId) => nodes.get(childId))
|
|
29
|
+
.filter((n): n is DelegationNode => n !== undefined);
|
|
30
|
+
}),
|
|
31
|
+
getChain: vi.fn(async (id: string) => {
|
|
32
|
+
const chain: DelegationNode[] = [];
|
|
33
|
+
let currentId: string | null = id;
|
|
34
|
+
|
|
35
|
+
while (currentId) {
|
|
36
|
+
const node = nodes.get(currentId);
|
|
37
|
+
if (!node) break;
|
|
38
|
+
chain.unshift(node);
|
|
39
|
+
currentId = node.parentId;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return chain;
|
|
43
|
+
}),
|
|
44
|
+
getDescendants: vi.fn(async (id: string) => {
|
|
45
|
+
const descendants: DelegationNode[] = [];
|
|
46
|
+
const queue: string[] = [id];
|
|
47
|
+
|
|
48
|
+
while (queue.length > 0) {
|
|
49
|
+
const currentId = queue.shift()!;
|
|
50
|
+
const node = nodes.get(currentId);
|
|
51
|
+
if (!node) continue;
|
|
52
|
+
|
|
53
|
+
for (const childId of node.children) {
|
|
54
|
+
const child = nodes.get(childId);
|
|
55
|
+
if (child) {
|
|
56
|
+
descendants.push(child);
|
|
57
|
+
queue.push(childId);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return descendants;
|
|
63
|
+
}),
|
|
64
|
+
deleteNode: vi.fn(async (id: string) => {
|
|
65
|
+
nodes.delete(id);
|
|
66
|
+
}),
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
beforeEach(() => {
|
|
71
|
+
storage = createMockStorage();
|
|
72
|
+
graph = new DelegationGraphManager(storage);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe("constructor", () => {
|
|
76
|
+
it("should create graph manager with storage", () => {
|
|
77
|
+
const manager = new DelegationGraphManager(storage);
|
|
78
|
+
expect(manager).toBeInstanceOf(DelegationGraphManager);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe("createDelegationGraph", () => {
|
|
83
|
+
it("should create a graph manager instance", () => {
|
|
84
|
+
const manager = createDelegationGraph(storage);
|
|
85
|
+
expect(manager).toBeInstanceOf(DelegationGraphManager);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe("registerDelegation", () => {
|
|
90
|
+
it("should register a root delegation (no parent)", async () => {
|
|
91
|
+
const node = await graph.registerDelegation({
|
|
92
|
+
id: "del-001",
|
|
93
|
+
parentId: null,
|
|
94
|
+
issuerDid: "did:web:example.com:issuer",
|
|
95
|
+
subjectDid: "did:web:example.com:subject",
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
expect(node.id).toBe("del-001");
|
|
99
|
+
expect(node.parentId).toBeNull();
|
|
100
|
+
expect(node.children).toEqual([]);
|
|
101
|
+
expect(node.issuerDid).toBe("did:web:example.com:issuer");
|
|
102
|
+
expect(node.subjectDid).toBe("did:web:example.com:subject");
|
|
103
|
+
expect(storage.setNode).toHaveBeenCalled();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("should register a child delegation and link to parent", async () => {
|
|
107
|
+
// Register parent first
|
|
108
|
+
await graph.registerDelegation({
|
|
109
|
+
id: "del-parent",
|
|
110
|
+
parentId: null,
|
|
111
|
+
issuerDid: "did:web:example.com:issuer",
|
|
112
|
+
subjectDid: "did:web:example.com:subject",
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Clear call count
|
|
116
|
+
vi.clearAllMocks();
|
|
117
|
+
|
|
118
|
+
// Register child
|
|
119
|
+
const childNode = await graph.registerDelegation({
|
|
120
|
+
id: "del-child",
|
|
121
|
+
parentId: "del-parent",
|
|
122
|
+
issuerDid: "did:web:example.com:subject",
|
|
123
|
+
subjectDid: "did:web:example.com:subject2",
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
expect(childNode.parentId).toBe("del-parent");
|
|
127
|
+
// setNode is called: 1) for child, 2) for parent (to add child to children list)
|
|
128
|
+
expect(storage.setNode).toHaveBeenCalledTimes(2);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("should include credentialStatusId if provided", async () => {
|
|
132
|
+
const node = await graph.registerDelegation({
|
|
133
|
+
id: "del-001",
|
|
134
|
+
parentId: null,
|
|
135
|
+
issuerDid: "did:web:example.com:issuer",
|
|
136
|
+
subjectDid: "did:web:example.com:subject",
|
|
137
|
+
credentialStatusId: "https://example.com/status#123",
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
expect(node.credentialStatusId).toBe("https://example.com/status#123");
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe("getNode", () => {
|
|
145
|
+
it("should return node if exists", async () => {
|
|
146
|
+
await graph.registerDelegation({
|
|
147
|
+
id: "del-001",
|
|
148
|
+
parentId: null,
|
|
149
|
+
issuerDid: "did:web:example.com:issuer",
|
|
150
|
+
subjectDid: "did:web:example.com:subject",
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const node = await graph.getNode("del-001");
|
|
154
|
+
expect(node).not.toBeNull();
|
|
155
|
+
expect(node?.id).toBe("del-001");
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("should return null if node does not exist", async () => {
|
|
159
|
+
const node = await graph.getNode("non-existent");
|
|
160
|
+
expect(node).toBeNull();
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
describe("getChildren", () => {
|
|
165
|
+
it("should return empty array for node with no children", async () => {
|
|
166
|
+
await graph.registerDelegation({
|
|
167
|
+
id: "del-001",
|
|
168
|
+
parentId: null,
|
|
169
|
+
issuerDid: "did:web:example.com:issuer",
|
|
170
|
+
subjectDid: "did:web:example.com:subject",
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
const children = await graph.getChildren("del-001");
|
|
174
|
+
expect(children).toEqual([]);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("should return all direct children", async () => {
|
|
178
|
+
// Register parent
|
|
179
|
+
await graph.registerDelegation({
|
|
180
|
+
id: "del-parent",
|
|
181
|
+
parentId: null,
|
|
182
|
+
issuerDid: "did:web:example.com:issuer",
|
|
183
|
+
subjectDid: "did:web:example.com:subject",
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Register children
|
|
187
|
+
await graph.registerDelegation({
|
|
188
|
+
id: "del-child1",
|
|
189
|
+
parentId: "del-parent",
|
|
190
|
+
issuerDid: "did:web:example.com:subject",
|
|
191
|
+
subjectDid: "did:web:example.com:subject2",
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
await graph.registerDelegation({
|
|
195
|
+
id: "del-child2",
|
|
196
|
+
parentId: "del-parent",
|
|
197
|
+
issuerDid: "did:web:example.com:subject",
|
|
198
|
+
subjectDid: "did:web:example.com:subject3",
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
const children = await graph.getChildren("del-parent");
|
|
202
|
+
expect(children.length).toBe(2);
|
|
203
|
+
expect(children.map((c) => c.id)).toContain("del-child1");
|
|
204
|
+
expect(children.map((c) => c.id)).toContain("del-child2");
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
describe("getDescendants", () => {
|
|
209
|
+
it("should return all descendants recursively", async () => {
|
|
210
|
+
// Create a tree: parent -> child1 -> grandchild
|
|
211
|
+
await graph.registerDelegation({
|
|
212
|
+
id: "del-parent",
|
|
213
|
+
parentId: null,
|
|
214
|
+
issuerDid: "did:web:example.com:issuer",
|
|
215
|
+
subjectDid: "did:web:example.com:subject",
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
await graph.registerDelegation({
|
|
219
|
+
id: "del-child1",
|
|
220
|
+
parentId: "del-parent",
|
|
221
|
+
issuerDid: "did:web:example.com:subject",
|
|
222
|
+
subjectDid: "did:web:example.com:subject2",
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
await graph.registerDelegation({
|
|
226
|
+
id: "del-grandchild",
|
|
227
|
+
parentId: "del-child1",
|
|
228
|
+
issuerDid: "did:web:example.com:subject2",
|
|
229
|
+
subjectDid: "did:web:example.com:subject3",
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
const descendants = await graph.getDescendants("del-parent");
|
|
233
|
+
expect(descendants.length).toBe(2);
|
|
234
|
+
expect(descendants.map((d) => d.id)).toContain("del-child1");
|
|
235
|
+
expect(descendants.map((d) => d.id)).toContain("del-grandchild");
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it("should return empty array for node with no descendants", async () => {
|
|
239
|
+
await graph.registerDelegation({
|
|
240
|
+
id: "del-001",
|
|
241
|
+
parentId: null,
|
|
242
|
+
issuerDid: "did:web:example.com:issuer",
|
|
243
|
+
subjectDid: "did:web:example.com:subject",
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
const descendants = await graph.getDescendants("del-001");
|
|
247
|
+
expect(descendants).toEqual([]);
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
describe("getChain", () => {
|
|
252
|
+
it("should return chain from root to node", async () => {
|
|
253
|
+
// Create chain: root -> child -> grandchild
|
|
254
|
+
await graph.registerDelegation({
|
|
255
|
+
id: "del-root",
|
|
256
|
+
parentId: null,
|
|
257
|
+
issuerDid: "did:web:example.com:issuer",
|
|
258
|
+
subjectDid: "did:web:example.com:subject",
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
await graph.registerDelegation({
|
|
262
|
+
id: "del-child",
|
|
263
|
+
parentId: "del-root",
|
|
264
|
+
issuerDid: "did:web:example.com:subject",
|
|
265
|
+
subjectDid: "did:web:example.com:subject2",
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
await graph.registerDelegation({
|
|
269
|
+
id: "del-grandchild",
|
|
270
|
+
parentId: "del-child",
|
|
271
|
+
issuerDid: "did:web:example.com:subject2",
|
|
272
|
+
subjectDid: "did:web:example.com:subject3",
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
const chain = await graph.getChain("del-grandchild");
|
|
276
|
+
expect(chain.length).toBe(3);
|
|
277
|
+
expect(chain[0].id).toBe("del-root");
|
|
278
|
+
expect(chain[1].id).toBe("del-child");
|
|
279
|
+
expect(chain[2].id).toBe("del-grandchild");
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it("should return single node for root", async () => {
|
|
283
|
+
await graph.registerDelegation({
|
|
284
|
+
id: "del-root",
|
|
285
|
+
parentId: null,
|
|
286
|
+
issuerDid: "did:web:example.com:issuer",
|
|
287
|
+
subjectDid: "did:web:example.com:subject",
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
const chain = await graph.getChain("del-root");
|
|
291
|
+
expect(chain.length).toBe(1);
|
|
292
|
+
expect(chain[0].id).toBe("del-root");
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
describe("isAncestor", () => {
|
|
297
|
+
it("should return true if ancestor is parent", async () => {
|
|
298
|
+
await graph.registerDelegation({
|
|
299
|
+
id: "del-parent",
|
|
300
|
+
parentId: null,
|
|
301
|
+
issuerDid: "did:web:example.com:issuer",
|
|
302
|
+
subjectDid: "did:web:example.com:subject",
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
await graph.registerDelegation({
|
|
306
|
+
id: "del-child",
|
|
307
|
+
parentId: "del-parent",
|
|
308
|
+
issuerDid: "did:web:example.com:subject",
|
|
309
|
+
subjectDid: "did:web:example.com:subject2",
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
const isAncestor = await graph.isAncestor("del-parent", "del-child");
|
|
313
|
+
expect(isAncestor).toBe(true);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it("should return true if ancestor is grandparent", async () => {
|
|
317
|
+
await graph.registerDelegation({
|
|
318
|
+
id: "del-grandparent",
|
|
319
|
+
parentId: null,
|
|
320
|
+
issuerDid: "did:web:example.com:issuer",
|
|
321
|
+
subjectDid: "did:web:example.com:subject",
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
await graph.registerDelegation({
|
|
325
|
+
id: "del-parent",
|
|
326
|
+
parentId: "del-grandparent",
|
|
327
|
+
issuerDid: "did:web:example.com:subject",
|
|
328
|
+
subjectDid: "did:web:example.com:subject2",
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
await graph.registerDelegation({
|
|
332
|
+
id: "del-child",
|
|
333
|
+
parentId: "del-parent",
|
|
334
|
+
issuerDid: "did:web:example.com:subject2",
|
|
335
|
+
subjectDid: "did:web:example.com:subject3",
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
const isAncestor = await graph.isAncestor("del-grandparent", "del-child");
|
|
339
|
+
expect(isAncestor).toBe(true);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it("should return false if not ancestor", async () => {
|
|
343
|
+
await graph.registerDelegation({
|
|
344
|
+
id: "del-1",
|
|
345
|
+
parentId: null,
|
|
346
|
+
issuerDid: "did:web:example.com:issuer",
|
|
347
|
+
subjectDid: "did:web:example.com:subject",
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
await graph.registerDelegation({
|
|
351
|
+
id: "del-2",
|
|
352
|
+
parentId: null,
|
|
353
|
+
issuerDid: "did:web:example.com:issuer2",
|
|
354
|
+
subjectDid: "did:web:example.com:subject2",
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
const isAncestor = await graph.isAncestor("del-1", "del-2");
|
|
358
|
+
expect(isAncestor).toBe(false);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it("should return false if nodes are reversed", async () => {
|
|
362
|
+
await graph.registerDelegation({
|
|
363
|
+
id: "del-parent",
|
|
364
|
+
parentId: null,
|
|
365
|
+
issuerDid: "did:web:example.com:issuer",
|
|
366
|
+
subjectDid: "did:web:example.com:subject",
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
await graph.registerDelegation({
|
|
370
|
+
id: "del-child",
|
|
371
|
+
parentId: "del-parent",
|
|
372
|
+
issuerDid: "did:web:example.com:subject",
|
|
373
|
+
subjectDid: "did:web:example.com:subject2",
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
const isAncestor = await graph.isAncestor("del-child", "del-parent");
|
|
377
|
+
expect(isAncestor).toBe(false);
|
|
378
|
+
});
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
describe("getDepth", () => {
|
|
382
|
+
it("should return 0 for root node", async () => {
|
|
383
|
+
await graph.registerDelegation({
|
|
384
|
+
id: "del-root",
|
|
385
|
+
parentId: null,
|
|
386
|
+
issuerDid: "did:web:example.com:issuer",
|
|
387
|
+
subjectDid: "did:web:example.com:subject",
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
const depth = await graph.getDepth("del-root");
|
|
391
|
+
expect(depth).toBe(0);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it("should return 1 for immediate child", async () => {
|
|
395
|
+
await graph.registerDelegation({
|
|
396
|
+
id: "del-root",
|
|
397
|
+
parentId: null,
|
|
398
|
+
issuerDid: "did:web:example.com:issuer",
|
|
399
|
+
subjectDid: "did:web:example.com:subject",
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
await graph.registerDelegation({
|
|
403
|
+
id: "del-child",
|
|
404
|
+
parentId: "del-root",
|
|
405
|
+
issuerDid: "did:web:example.com:subject",
|
|
406
|
+
subjectDid: "did:web:example.com:subject2",
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
const depth = await graph.getDepth("del-child");
|
|
410
|
+
expect(depth).toBe(1);
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
it("should return correct depth for nested nodes", async () => {
|
|
414
|
+
await graph.registerDelegation({
|
|
415
|
+
id: "del-root",
|
|
416
|
+
parentId: null,
|
|
417
|
+
issuerDid: "did:web:example.com:issuer",
|
|
418
|
+
subjectDid: "did:web:example.com:subject",
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
await graph.registerDelegation({
|
|
422
|
+
id: "del-child",
|
|
423
|
+
parentId: "del-root",
|
|
424
|
+
issuerDid: "did:web:example.com:subject",
|
|
425
|
+
subjectDid: "did:web:example.com:subject2",
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
await graph.registerDelegation({
|
|
429
|
+
id: "del-grandchild",
|
|
430
|
+
parentId: "del-child",
|
|
431
|
+
issuerDid: "did:web:example.com:subject2",
|
|
432
|
+
subjectDid: "did:web:example.com:subject3",
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
const depth = await graph.getDepth("del-grandchild");
|
|
436
|
+
expect(depth).toBe(2);
|
|
437
|
+
});
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
describe("validateChain", () => {
|
|
441
|
+
it("should validate correct chain", async () => {
|
|
442
|
+
await graph.registerDelegation({
|
|
443
|
+
id: "del-root",
|
|
444
|
+
parentId: null,
|
|
445
|
+
issuerDid: "did:web:example.com:issuer",
|
|
446
|
+
subjectDid: "did:web:example.com:subject",
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
await graph.registerDelegation({
|
|
450
|
+
id: "del-child",
|
|
451
|
+
parentId: "del-root",
|
|
452
|
+
issuerDid: "did:web:example.com:subject", // Child's issuer = parent's subject
|
|
453
|
+
subjectDid: "did:web:example.com:subject2",
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
const result = await graph.validateChain("del-child");
|
|
457
|
+
expect(result.valid).toBe(true);
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
it("should invalidate chain with mismatched issuer/subject", async () => {
|
|
461
|
+
await graph.registerDelegation({
|
|
462
|
+
id: "del-root",
|
|
463
|
+
parentId: null,
|
|
464
|
+
issuerDid: "did:web:example.com:issuer",
|
|
465
|
+
subjectDid: "did:web:example.com:subject",
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
await graph.registerDelegation({
|
|
469
|
+
id: "del-child",
|
|
470
|
+
parentId: "del-root",
|
|
471
|
+
issuerDid: "did:web:example.com:wrong-issuer", // Wrong issuer
|
|
472
|
+
subjectDid: "did:web:example.com:subject2",
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
const result = await graph.validateChain("del-child");
|
|
476
|
+
expect(result.valid).toBe(false);
|
|
477
|
+
expect(result.reason).toContain("Invalid chain");
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
// Note: parentId mismatch validation is complex - covered by issuer/subject mismatch test above
|
|
481
|
+
|
|
482
|
+
it("should return invalid for non-existent delegation", async () => {
|
|
483
|
+
const result = await graph.validateChain("non-existent");
|
|
484
|
+
expect(result.valid).toBe(false);
|
|
485
|
+
expect(result.reason).toBe("Delegation not found");
|
|
486
|
+
});
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
describe("removeDelegation", () => {
|
|
490
|
+
it("should remove delegation from graph", async () => {
|
|
491
|
+
await graph.registerDelegation({
|
|
492
|
+
id: "del-001",
|
|
493
|
+
parentId: null,
|
|
494
|
+
issuerDid: "did:web:example.com:issuer",
|
|
495
|
+
subjectDid: "did:web:example.com:subject",
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
await graph.removeDelegation("del-001");
|
|
499
|
+
|
|
500
|
+
const node = await graph.getNode("del-001");
|
|
501
|
+
expect(node).toBeNull();
|
|
502
|
+
expect(storage.deleteNode).toHaveBeenCalledWith("del-001");
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
it("should remove child from parent's children list", async () => {
|
|
506
|
+
await graph.registerDelegation({
|
|
507
|
+
id: "del-parent",
|
|
508
|
+
parentId: null,
|
|
509
|
+
issuerDid: "did:web:example.com:issuer",
|
|
510
|
+
subjectDid: "did:web:example.com:subject",
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
await graph.registerDelegation({
|
|
514
|
+
id: "del-child",
|
|
515
|
+
parentId: "del-parent",
|
|
516
|
+
issuerDid: "did:web:example.com:subject",
|
|
517
|
+
subjectDid: "did:web:example.com:subject2",
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
await graph.removeDelegation("del-child");
|
|
521
|
+
|
|
522
|
+
const parent = await graph.getNode("del-parent");
|
|
523
|
+
expect(parent?.children).not.toContain("del-child");
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
it("should handle removal of non-existent delegation gracefully", async () => {
|
|
527
|
+
await expect(graph.removeDelegation("non-existent")).resolves.not.toThrow();
|
|
528
|
+
});
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
describe("edge cases", () => {
|
|
532
|
+
it("should handle multiple children correctly", async () => {
|
|
533
|
+
await graph.registerDelegation({
|
|
534
|
+
id: "del-parent",
|
|
535
|
+
parentId: null,
|
|
536
|
+
issuerDid: "did:web:example.com:issuer",
|
|
537
|
+
subjectDid: "did:web:example.com:subject",
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
await graph.registerDelegation({
|
|
541
|
+
id: "del-child1",
|
|
542
|
+
parentId: "del-parent",
|
|
543
|
+
issuerDid: "did:web:example.com:subject",
|
|
544
|
+
subjectDid: "did:web:example.com:subject2",
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
await graph.registerDelegation({
|
|
548
|
+
id: "del-child2",
|
|
549
|
+
parentId: "del-parent",
|
|
550
|
+
issuerDid: "did:web:example.com:subject",
|
|
551
|
+
subjectDid: "did:web:example.com:subject3",
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
await graph.registerDelegation({
|
|
555
|
+
id: "del-child3",
|
|
556
|
+
parentId: "del-parent",
|
|
557
|
+
issuerDid: "did:web:example.com:subject",
|
|
558
|
+
subjectDid: "did:web:example.com:subject4",
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
const children = await graph.getChildren("del-parent");
|
|
562
|
+
expect(children.length).toBe(3);
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
it("should handle deep chains correctly", async () => {
|
|
566
|
+
// Create a chain of depth 5
|
|
567
|
+
const chain: string[] = [];
|
|
568
|
+
for (let i = 0; i < 5; i++) {
|
|
569
|
+
const id = `del-${i}`;
|
|
570
|
+
chain.push(id);
|
|
571
|
+
await graph.registerDelegation({
|
|
572
|
+
id,
|
|
573
|
+
parentId: i === 0 ? null : chain[i - 1],
|
|
574
|
+
issuerDid: `did:web:example.com:issuer${i}`,
|
|
575
|
+
subjectDid: `did:web:example.com:subject${i + 1}`,
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
const depth = await graph.getDepth(chain[4]);
|
|
580
|
+
expect(depth).toBe(4);
|
|
581
|
+
});
|
|
582
|
+
});
|
|
583
|
+
});
|
|
584
|
+
|