@kya-os/mcp-i-core 1.2.3-canary.7 → 1.3.1

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.
Files changed (225) hide show
  1. package/.claude/settings.local.json +9 -0
  2. package/.turbo/turbo-build.log +4 -0
  3. package/.turbo/turbo-test.log +2979 -0
  4. package/COMPLIANCE_IMPROVEMENT_REPORT.md +483 -0
  5. package/Composer 3.md +615 -0
  6. package/GPT-5.md +1169 -0
  7. package/OPUS-plan.md +352 -0
  8. package/PHASE_3_AND_4.1_SUMMARY.md +585 -0
  9. package/PHASE_3_SUMMARY.md +317 -0
  10. package/PHASE_4.1.3_SUMMARY.md +428 -0
  11. package/PHASE_4.1_COMPLETE.md +525 -0
  12. package/PHASE_4_USER_DID_IDENTITY_LINKING_PLAN.md +1240 -0
  13. package/SCHEMA_COMPLIANCE_REPORT.md +275 -0
  14. package/TEST_PLAN.md +571 -0
  15. package/coverage/coverage-final.json +57 -0
  16. package/dist/__tests__/utils/mock-providers.d.ts +1 -2
  17. package/dist/__tests__/utils/mock-providers.d.ts.map +1 -1
  18. package/dist/__tests__/utils/mock-providers.js.map +1 -1
  19. package/dist/cache/oauth-config-cache.d.ts +69 -0
  20. package/dist/cache/oauth-config-cache.d.ts.map +1 -0
  21. package/dist/cache/oauth-config-cache.js +76 -0
  22. package/dist/cache/oauth-config-cache.js.map +1 -0
  23. package/dist/identity/idp-token-resolver.d.ts +53 -0
  24. package/dist/identity/idp-token-resolver.d.ts.map +1 -0
  25. package/dist/identity/idp-token-resolver.js +108 -0
  26. package/dist/identity/idp-token-resolver.js.map +1 -0
  27. package/dist/identity/idp-token-storage.interface.d.ts +42 -0
  28. package/dist/identity/idp-token-storage.interface.d.ts.map +1 -0
  29. package/dist/identity/idp-token-storage.interface.js +12 -0
  30. package/dist/identity/idp-token-storage.interface.js.map +1 -0
  31. package/dist/identity/user-did-manager.d.ts +39 -1
  32. package/dist/identity/user-did-manager.d.ts.map +1 -1
  33. package/dist/identity/user-did-manager.js +69 -3
  34. package/dist/identity/user-did-manager.js.map +1 -1
  35. package/dist/index.d.ts +22 -0
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +39 -1
  38. package/dist/index.js.map +1 -1
  39. package/dist/runtime/audit-logger.d.ts +37 -0
  40. package/dist/runtime/audit-logger.d.ts.map +1 -0
  41. package/dist/runtime/audit-logger.js +9 -0
  42. package/dist/runtime/audit-logger.js.map +1 -0
  43. package/dist/runtime/base.d.ts +58 -2
  44. package/dist/runtime/base.d.ts.map +1 -1
  45. package/dist/runtime/base.js +266 -11
  46. package/dist/runtime/base.js.map +1 -1
  47. package/dist/services/access-control.service.d.ts.map +1 -1
  48. package/dist/services/access-control.service.js +200 -35
  49. package/dist/services/access-control.service.js.map +1 -1
  50. package/dist/services/authorization/authorization-registry.d.ts +29 -0
  51. package/dist/services/authorization/authorization-registry.d.ts.map +1 -0
  52. package/dist/services/authorization/authorization-registry.js +57 -0
  53. package/dist/services/authorization/authorization-registry.js.map +1 -0
  54. package/dist/services/authorization/types.d.ts +53 -0
  55. package/dist/services/authorization/types.d.ts.map +1 -0
  56. package/dist/services/authorization/types.js +10 -0
  57. package/dist/services/authorization/types.js.map +1 -0
  58. package/dist/services/batch-delegation.service.d.ts +53 -0
  59. package/dist/services/batch-delegation.service.d.ts.map +1 -0
  60. package/dist/services/batch-delegation.service.js +95 -0
  61. package/dist/services/batch-delegation.service.js.map +1 -0
  62. package/dist/services/oauth-config.service.d.ts +53 -0
  63. package/dist/services/oauth-config.service.d.ts.map +1 -0
  64. package/dist/services/oauth-config.service.js +119 -0
  65. package/dist/services/oauth-config.service.js.map +1 -0
  66. package/dist/services/oauth-provider-registry.d.ts +88 -0
  67. package/dist/services/oauth-provider-registry.d.ts.map +1 -0
  68. package/dist/services/oauth-provider-registry.js +128 -0
  69. package/dist/services/oauth-provider-registry.js.map +1 -0
  70. package/dist/services/oauth-service.d.ts +77 -0
  71. package/dist/services/oauth-service.d.ts.map +1 -0
  72. package/dist/services/oauth-service.js +348 -0
  73. package/dist/services/oauth-service.js.map +1 -0
  74. package/dist/services/oauth-token-retrieval.service.d.ts +49 -0
  75. package/dist/services/oauth-token-retrieval.service.d.ts.map +1 -0
  76. package/dist/services/oauth-token-retrieval.service.js +150 -0
  77. package/dist/services/oauth-token-retrieval.service.js.map +1 -0
  78. package/dist/services/provider-resolver.d.ts +48 -0
  79. package/dist/services/provider-resolver.d.ts.map +1 -0
  80. package/dist/services/provider-resolver.js +121 -0
  81. package/dist/services/provider-resolver.js.map +1 -0
  82. package/dist/services/provider-validator.d.ts +55 -0
  83. package/dist/services/provider-validator.d.ts.map +1 -0
  84. package/dist/services/provider-validator.js +135 -0
  85. package/dist/services/provider-validator.js.map +1 -0
  86. package/dist/services/tool-context-builder.d.ts +57 -0
  87. package/dist/services/tool-context-builder.d.ts.map +1 -0
  88. package/dist/services/tool-context-builder.js +125 -0
  89. package/dist/services/tool-context-builder.js.map +1 -0
  90. package/dist/services/tool-protection.service.d.ts +87 -10
  91. package/dist/services/tool-protection.service.d.ts.map +1 -1
  92. package/dist/services/tool-protection.service.js +282 -112
  93. package/dist/services/tool-protection.service.js.map +1 -1
  94. package/dist/types/oauth-required-error.d.ts +40 -0
  95. package/dist/types/oauth-required-error.d.ts.map +1 -0
  96. package/dist/types/oauth-required-error.js +40 -0
  97. package/dist/types/oauth-required-error.js.map +1 -0
  98. package/dist/utils/did-helpers.d.ts +33 -0
  99. package/dist/utils/did-helpers.d.ts.map +1 -1
  100. package/dist/utils/did-helpers.js +40 -0
  101. package/dist/utils/did-helpers.js.map +1 -1
  102. package/dist/utils/index.d.ts +1 -0
  103. package/dist/utils/index.d.ts.map +1 -1
  104. package/dist/utils/index.js +1 -0
  105. package/dist/utils/index.js.map +1 -1
  106. package/docs/API_REFERENCE.md +1362 -0
  107. package/docs/COMPLIANCE_MATRIX.md +691 -0
  108. package/docs/STATUSLIST2021_GUIDE.md +696 -0
  109. package/docs/W3C_VC_DELEGATION_GUIDE.md +710 -0
  110. package/package.json +24 -50
  111. package/scripts/audit-compliance.ts +724 -0
  112. package/src/__tests__/cache/tool-protection-cache.test.ts +640 -0
  113. package/src/__tests__/config/provider-runtime-config.test.ts +309 -0
  114. package/src/__tests__/delegation-e2e.test.ts +690 -0
  115. package/src/__tests__/identity/user-did-manager.test.ts +213 -0
  116. package/src/__tests__/index.test.ts +56 -0
  117. package/src/__tests__/integration/full-flow.test.ts +776 -0
  118. package/src/__tests__/integration.test.ts +281 -0
  119. package/src/__tests__/providers/base.test.ts +173 -0
  120. package/src/__tests__/providers/memory.test.ts +319 -0
  121. package/src/__tests__/regression/phase2-regression.test.ts +429 -0
  122. package/src/__tests__/runtime/audit-logger.test.ts +154 -0
  123. package/src/__tests__/runtime/base-extensions.test.ts +593 -0
  124. package/src/__tests__/runtime/base.test.ts +869 -0
  125. package/src/__tests__/runtime/delegation-flow.test.ts +164 -0
  126. package/src/__tests__/runtime/proof-client-did.test.ts +375 -0
  127. package/src/__tests__/runtime/route-interception.test.ts +686 -0
  128. package/src/__tests__/runtime/tool-protection-enforcement.test.ts +908 -0
  129. package/src/__tests__/services/agentshield-integration.test.ts +784 -0
  130. package/src/__tests__/services/provider-resolver-edge-cases.test.ts +591 -0
  131. package/src/__tests__/services/tool-protection-oauth-provider.test.ts +480 -0
  132. package/src/__tests__/services/tool-protection.service.test.ts +1366 -0
  133. package/src/__tests__/utils/mock-providers.ts +340 -0
  134. package/src/cache/oauth-config-cache.d.ts +69 -0
  135. package/src/cache/oauth-config-cache.d.ts.map +1 -0
  136. package/src/cache/oauth-config-cache.js.map +1 -0
  137. package/src/cache/oauth-config-cache.ts +123 -0
  138. package/src/cache/tool-protection-cache.ts +171 -0
  139. package/src/compliance/EXAMPLE.md +412 -0
  140. package/src/compliance/__tests__/schema-verifier.test.ts +797 -0
  141. package/src/compliance/index.ts +8 -0
  142. package/src/compliance/schema-registry.ts +460 -0
  143. package/src/compliance/schema-verifier.ts +708 -0
  144. package/src/config/__tests__/remote-config.spec.ts +268 -0
  145. package/src/config/remote-config.ts +174 -0
  146. package/src/config.ts +309 -0
  147. package/src/delegation/__tests__/audience-validator.test.ts +112 -0
  148. package/src/delegation/__tests__/bitstring.test.ts +346 -0
  149. package/src/delegation/__tests__/cascading-revocation.test.ts +628 -0
  150. package/src/delegation/__tests__/delegation-graph.test.ts +584 -0
  151. package/src/delegation/__tests__/utils.test.ts +152 -0
  152. package/src/delegation/__tests__/vc-issuer.test.ts +442 -0
  153. package/src/delegation/__tests__/vc-verifier.test.ts +922 -0
  154. package/src/delegation/audience-validator.ts +52 -0
  155. package/src/delegation/bitstring.ts +278 -0
  156. package/src/delegation/cascading-revocation.ts +370 -0
  157. package/src/delegation/delegation-graph.ts +299 -0
  158. package/src/delegation/index.ts +14 -0
  159. package/src/delegation/statuslist-manager.ts +353 -0
  160. package/src/delegation/storage/__tests__/memory-graph-storage.test.ts +366 -0
  161. package/src/delegation/storage/__tests__/memory-statuslist-storage.test.ts +228 -0
  162. package/src/delegation/storage/index.ts +9 -0
  163. package/src/delegation/storage/memory-graph-storage.ts +178 -0
  164. package/src/delegation/storage/memory-statuslist-storage.ts +77 -0
  165. package/src/delegation/utils.ts +42 -0
  166. package/src/delegation/vc-issuer.ts +232 -0
  167. package/src/delegation/vc-verifier.ts +568 -0
  168. package/src/identity/idp-token-resolver.ts +147 -0
  169. package/src/identity/idp-token-storage.interface.ts +59 -0
  170. package/src/identity/user-did-manager.ts +370 -0
  171. package/src/index.ts +260 -0
  172. package/src/providers/base.d.ts +91 -0
  173. package/src/providers/base.d.ts.map +1 -0
  174. package/src/providers/base.js.map +1 -0
  175. package/src/providers/base.ts +96 -0
  176. package/src/providers/memory.ts +142 -0
  177. package/src/runtime/audit-logger.ts +39 -0
  178. package/src/runtime/base.ts +1329 -0
  179. package/src/services/__tests__/access-control.integration.test.ts +443 -0
  180. package/src/services/__tests__/access-control.proof-response-validation.test.ts +578 -0
  181. package/src/services/__tests__/access-control.service.test.ts +970 -0
  182. package/src/services/__tests__/batch-delegation.service.test.ts +351 -0
  183. package/src/services/__tests__/crypto.service.test.ts +531 -0
  184. package/src/services/__tests__/oauth-provider-registry.test.ts +142 -0
  185. package/src/services/__tests__/proof-verifier.integration.test.ts +485 -0
  186. package/src/services/__tests__/proof-verifier.test.ts +489 -0
  187. package/src/services/__tests__/provider-resolution.integration.test.ts +202 -0
  188. package/src/services/__tests__/provider-resolver.test.ts +213 -0
  189. package/src/services/__tests__/storage.service.test.ts +358 -0
  190. package/src/services/access-control.service.ts +990 -0
  191. package/src/services/authorization/authorization-registry.ts +66 -0
  192. package/src/services/authorization/types.ts +71 -0
  193. package/src/services/batch-delegation.service.ts +137 -0
  194. package/src/services/crypto.service.ts +302 -0
  195. package/src/services/errors.ts +76 -0
  196. package/src/services/index.ts +9 -0
  197. package/src/services/oauth-config.service.d.ts +53 -0
  198. package/src/services/oauth-config.service.d.ts.map +1 -0
  199. package/src/services/oauth-config.service.js.map +1 -0
  200. package/src/services/oauth-config.service.ts +169 -0
  201. package/src/services/oauth-provider-registry.d.ts +57 -0
  202. package/src/services/oauth-provider-registry.d.ts.map +1 -0
  203. package/src/services/oauth-provider-registry.js.map +1 -0
  204. package/src/services/oauth-provider-registry.ts +141 -0
  205. package/src/services/oauth-service.ts +510 -0
  206. package/src/services/oauth-token-retrieval.service.ts +245 -0
  207. package/src/services/proof-verifier.ts +478 -0
  208. package/src/services/provider-resolver.d.ts +48 -0
  209. package/src/services/provider-resolver.d.ts.map +1 -0
  210. package/src/services/provider-resolver.js.map +1 -0
  211. package/src/services/provider-resolver.ts +146 -0
  212. package/src/services/provider-validator.ts +170 -0
  213. package/src/services/storage.service.ts +566 -0
  214. package/src/services/tool-context-builder.ts +172 -0
  215. package/src/services/tool-protection.service.ts +958 -0
  216. package/src/types/oauth-required-error.ts +63 -0
  217. package/src/types/tool-protection.ts +155 -0
  218. package/src/utils/__tests__/did-helpers.test.ts +101 -0
  219. package/src/utils/base64.ts +148 -0
  220. package/src/utils/cors.ts +83 -0
  221. package/src/utils/did-helpers.ts +150 -0
  222. package/src/utils/index.ts +8 -0
  223. package/src/utils/storage-keys.ts +278 -0
  224. package/tsconfig.json +21 -0
  225. 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
+