id.org.ai 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.
@@ -0,0 +1,356 @@
1
+ /**
2
+ * AgentIdentity Tests - RED PHASE
3
+ *
4
+ * Tests for the AgentIdentity type which extends Identity for AI agents.
5
+ * These tests WILL FAIL until the implementation is complete in GREEN phase.
6
+ */
7
+
8
+ import { describe, it, expect } from 'vitest'
9
+ import {
10
+ AgentIdentity,
11
+ AgentIdentitySchema,
12
+ isAgentIdentity,
13
+ createAgentIdentity,
14
+ } from '../src'
15
+
16
+ describe('AgentIdentity', () => {
17
+ describe('interface shape', () => {
18
+ it('should have required $id field', () => {
19
+ const agent: AgentIdentity = {
20
+ $id: 'https://example.com/agents/1',
21
+ $type: 'https://schema.org.ai/AgentIdentity',
22
+ model: 'claude-3-opus',
23
+ capabilities: ['text', 'code'],
24
+ autonomous: false,
25
+ createdAt: new Date().toISOString(),
26
+ updatedAt: new Date().toISOString(),
27
+ }
28
+ expect(agent.$id).toBe('https://example.com/agents/1')
29
+ })
30
+
31
+ it('should have required $type field with correct value', () => {
32
+ const agent: AgentIdentity = {
33
+ $id: 'https://example.com/agents/1',
34
+ $type: 'https://schema.org.ai/AgentIdentity',
35
+ model: 'claude-3-opus',
36
+ capabilities: ['text'],
37
+ autonomous: false,
38
+ createdAt: new Date().toISOString(),
39
+ updatedAt: new Date().toISOString(),
40
+ }
41
+ expect(agent.$type).toBe('https://schema.org.ai/AgentIdentity')
42
+ })
43
+
44
+ it('should have required model field', () => {
45
+ const agent: AgentIdentity = {
46
+ $id: 'https://example.com/agents/1',
47
+ $type: 'https://schema.org.ai/AgentIdentity',
48
+ model: 'claude-3-opus',
49
+ capabilities: ['text'],
50
+ autonomous: false,
51
+ createdAt: new Date().toISOString(),
52
+ updatedAt: new Date().toISOString(),
53
+ }
54
+ expect(agent.model).toBe('claude-3-opus')
55
+ })
56
+
57
+ it('should have required capabilities array', () => {
58
+ const agent: AgentIdentity = {
59
+ $id: 'https://example.com/agents/1',
60
+ $type: 'https://schema.org.ai/AgentIdentity',
61
+ model: 'claude-3-opus',
62
+ capabilities: ['text', 'code', 'vision'],
63
+ autonomous: false,
64
+ createdAt: new Date().toISOString(),
65
+ updatedAt: new Date().toISOString(),
66
+ }
67
+ expect(agent.capabilities).toEqual(['text', 'code', 'vision'])
68
+ })
69
+
70
+ it('should have required autonomous boolean', () => {
71
+ const agent: AgentIdentity = {
72
+ $id: 'https://example.com/agents/1',
73
+ $type: 'https://schema.org.ai/AgentIdentity',
74
+ model: 'claude-3-opus',
75
+ capabilities: ['text'],
76
+ autonomous: true,
77
+ createdAt: new Date().toISOString(),
78
+ updatedAt: new Date().toISOString(),
79
+ }
80
+ expect(agent.autonomous).toBe(true)
81
+ })
82
+
83
+ it('should inherit createdAt and updatedAt from Identity', () => {
84
+ const now = new Date().toISOString()
85
+ const agent: AgentIdentity = {
86
+ $id: 'https://example.com/agents/1',
87
+ $type: 'https://schema.org.ai/AgentIdentity',
88
+ model: 'claude-3-opus',
89
+ capabilities: ['text'],
90
+ autonomous: false,
91
+ createdAt: now,
92
+ updatedAt: now,
93
+ }
94
+ expect(agent.createdAt).toBe(now)
95
+ expect(agent.updatedAt).toBe(now)
96
+ })
97
+ })
98
+
99
+ describe('Zod schema validation', () => {
100
+ it('should validate a valid AgentIdentity object', () => {
101
+ const result = AgentIdentitySchema.safeParse({
102
+ $id: 'https://example.com/agents/1',
103
+ $type: 'https://schema.org.ai/AgentIdentity',
104
+ model: 'claude-3-opus',
105
+ capabilities: ['text', 'code'],
106
+ autonomous: false,
107
+ createdAt: new Date().toISOString(),
108
+ updatedAt: new Date().toISOString(),
109
+ })
110
+ expect(result.success).toBe(true)
111
+ })
112
+
113
+ it('should validate AgentIdentity with empty capabilities', () => {
114
+ const result = AgentIdentitySchema.safeParse({
115
+ $id: 'https://example.com/agents/1',
116
+ $type: 'https://schema.org.ai/AgentIdentity',
117
+ model: 'gpt-4',
118
+ capabilities: [],
119
+ autonomous: false,
120
+ createdAt: new Date().toISOString(),
121
+ updatedAt: new Date().toISOString(),
122
+ })
123
+ expect(result.success).toBe(true)
124
+ })
125
+
126
+ it('should reject AgentIdentity without model', () => {
127
+ const result = AgentIdentitySchema.safeParse({
128
+ $id: 'https://example.com/agents/1',
129
+ $type: 'https://schema.org.ai/AgentIdentity',
130
+ capabilities: ['text'],
131
+ autonomous: false,
132
+ createdAt: new Date().toISOString(),
133
+ updatedAt: new Date().toISOString(),
134
+ })
135
+ expect(result.success).toBe(false)
136
+ })
137
+
138
+ it('should reject AgentIdentity without capabilities', () => {
139
+ const result = AgentIdentitySchema.safeParse({
140
+ $id: 'https://example.com/agents/1',
141
+ $type: 'https://schema.org.ai/AgentIdentity',
142
+ model: 'claude-3-opus',
143
+ autonomous: false,
144
+ createdAt: new Date().toISOString(),
145
+ updatedAt: new Date().toISOString(),
146
+ })
147
+ expect(result.success).toBe(false)
148
+ })
149
+
150
+ it('should reject AgentIdentity with non-array capabilities', () => {
151
+ const result = AgentIdentitySchema.safeParse({
152
+ $id: 'https://example.com/agents/1',
153
+ $type: 'https://schema.org.ai/AgentIdentity',
154
+ model: 'claude-3-opus',
155
+ capabilities: 'text',
156
+ autonomous: false,
157
+ createdAt: new Date().toISOString(),
158
+ updatedAt: new Date().toISOString(),
159
+ })
160
+ expect(result.success).toBe(false)
161
+ })
162
+
163
+ it('should reject AgentIdentity without autonomous', () => {
164
+ const result = AgentIdentitySchema.safeParse({
165
+ $id: 'https://example.com/agents/1',
166
+ $type: 'https://schema.org.ai/AgentIdentity',
167
+ model: 'claude-3-opus',
168
+ capabilities: ['text'],
169
+ createdAt: new Date().toISOString(),
170
+ updatedAt: new Date().toISOString(),
171
+ })
172
+ expect(result.success).toBe(false)
173
+ })
174
+
175
+ it('should reject AgentIdentity with non-boolean autonomous', () => {
176
+ const result = AgentIdentitySchema.safeParse({
177
+ $id: 'https://example.com/agents/1',
178
+ $type: 'https://schema.org.ai/AgentIdentity',
179
+ model: 'claude-3-opus',
180
+ capabilities: ['text'],
181
+ autonomous: 'yes',
182
+ createdAt: new Date().toISOString(),
183
+ updatedAt: new Date().toISOString(),
184
+ })
185
+ expect(result.success).toBe(false)
186
+ })
187
+
188
+ it('should reject AgentIdentity with wrong $type', () => {
189
+ const result = AgentIdentitySchema.safeParse({
190
+ $id: 'https://example.com/agents/1',
191
+ $type: 'https://schema.org.ai/User',
192
+ model: 'claude-3-opus',
193
+ capabilities: ['text'],
194
+ autonomous: false,
195
+ createdAt: new Date().toISOString(),
196
+ updatedAt: new Date().toISOString(),
197
+ })
198
+ expect(result.success).toBe(false)
199
+ })
200
+
201
+ it('should reject completely invalid data', () => {
202
+ const result = AgentIdentitySchema.safeParse({ invalid: 'data' })
203
+ expect(result.success).toBe(false)
204
+ })
205
+
206
+ it('should reject non-object values', () => {
207
+ expect(AgentIdentitySchema.safeParse(null).success).toBe(false)
208
+ expect(AgentIdentitySchema.safeParse(undefined).success).toBe(false)
209
+ expect(AgentIdentitySchema.safeParse('string').success).toBe(false)
210
+ expect(AgentIdentitySchema.safeParse(123).success).toBe(false)
211
+ })
212
+ })
213
+
214
+ describe('isAgentIdentity type guard', () => {
215
+ it('should return true for valid AgentIdentity', () => {
216
+ const agent = {
217
+ $id: 'https://example.com/agents/1',
218
+ $type: 'https://schema.org.ai/AgentIdentity',
219
+ model: 'claude-3-opus',
220
+ capabilities: ['text', 'code'],
221
+ autonomous: false,
222
+ createdAt: new Date().toISOString(),
223
+ updatedAt: new Date().toISOString(),
224
+ }
225
+ expect(isAgentIdentity(agent)).toBe(true)
226
+ })
227
+
228
+ it('should return true for autonomous AgentIdentity', () => {
229
+ const agent = {
230
+ $id: 'https://example.com/agents/1',
231
+ $type: 'https://schema.org.ai/AgentIdentity',
232
+ model: 'claude-3-opus',
233
+ capabilities: ['text'],
234
+ autonomous: true,
235
+ createdAt: new Date().toISOString(),
236
+ updatedAt: new Date().toISOString(),
237
+ }
238
+ expect(isAgentIdentity(agent)).toBe(true)
239
+ })
240
+
241
+ it('should return false for invalid data', () => {
242
+ expect(isAgentIdentity({ invalid: 'data' })).toBe(false)
243
+ })
244
+
245
+ it('should return false for null', () => {
246
+ expect(isAgentIdentity(null)).toBe(false)
247
+ })
248
+
249
+ it('should return false for undefined', () => {
250
+ expect(isAgentIdentity(undefined)).toBe(false)
251
+ })
252
+
253
+ it('should return false for Identity (base type)', () => {
254
+ const identity = {
255
+ $id: 'https://example.com/identities/1',
256
+ $type: 'https://schema.org.ai/Identity',
257
+ createdAt: new Date().toISOString(),
258
+ updatedAt: new Date().toISOString(),
259
+ }
260
+ expect(isAgentIdentity(identity)).toBe(false)
261
+ })
262
+
263
+ it('should return false for User', () => {
264
+ const user = {
265
+ $id: 'https://example.com/users/1',
266
+ $type: 'https://schema.org.ai/User',
267
+ email: 'test@example.com',
268
+ name: 'Test User',
269
+ createdAt: new Date().toISOString(),
270
+ updatedAt: new Date().toISOString(),
271
+ }
272
+ expect(isAgentIdentity(user)).toBe(false)
273
+ })
274
+ })
275
+
276
+ describe('createAgentIdentity factory', () => {
277
+ it('should create a valid AgentIdentity with required fields', () => {
278
+ const agent = createAgentIdentity({
279
+ model: 'claude-3-opus',
280
+ capabilities: ['text', 'code'],
281
+ autonomous: false,
282
+ })
283
+ expect(agent.$type).toBe('https://schema.org.ai/AgentIdentity')
284
+ expect(agent.model).toBe('claude-3-opus')
285
+ expect(agent.capabilities).toEqual(['text', 'code'])
286
+ expect(agent.autonomous).toBe(false)
287
+ })
288
+
289
+ it('should auto-generate $id', () => {
290
+ const agent = createAgentIdentity({
291
+ model: 'gpt-4',
292
+ capabilities: ['text'],
293
+ autonomous: true,
294
+ })
295
+ expect(agent.$id).toBeDefined()
296
+ expect(agent.$id).toMatch(/^https:\/\//)
297
+ })
298
+
299
+ it('should auto-generate timestamps', () => {
300
+ const before = new Date().toISOString()
301
+ const agent = createAgentIdentity({
302
+ model: 'claude-3-opus',
303
+ capabilities: ['text'],
304
+ autonomous: false,
305
+ })
306
+ const after = new Date().toISOString()
307
+ expect(agent.createdAt >= before).toBe(true)
308
+ expect(agent.createdAt <= after).toBe(true)
309
+ expect(agent.updatedAt >= before).toBe(true)
310
+ expect(agent.updatedAt <= after).toBe(true)
311
+ })
312
+
313
+ it('should allow custom $id override', () => {
314
+ const customId = 'https://custom.com/agents/custom-1'
315
+ const agent = createAgentIdentity({
316
+ $id: customId,
317
+ model: 'claude-3-opus',
318
+ capabilities: ['text'],
319
+ autonomous: false,
320
+ })
321
+ expect(agent.$id).toBe(customId)
322
+ })
323
+
324
+ it('should create autonomous agent when specified', () => {
325
+ const agent = createAgentIdentity({
326
+ model: 'claude-3-opus',
327
+ capabilities: ['text', 'code', 'execute'],
328
+ autonomous: true,
329
+ })
330
+ expect(agent.autonomous).toBe(true)
331
+ })
332
+
333
+ it('should pass validation after creation', () => {
334
+ const agent = createAgentIdentity({
335
+ model: 'claude-3-opus',
336
+ capabilities: ['text'],
337
+ autonomous: false,
338
+ })
339
+ expect(isAgentIdentity(agent)).toBe(true)
340
+ expect(AgentIdentitySchema.safeParse(agent).success).toBe(true)
341
+ })
342
+
343
+ it('should support various model names', () => {
344
+ const models = ['claude-3-opus', 'claude-3-sonnet', 'gpt-4', 'gpt-4-turbo', 'gemini-pro']
345
+ for (const model of models) {
346
+ const agent = createAgentIdentity({
347
+ model,
348
+ capabilities: ['text'],
349
+ autonomous: false,
350
+ })
351
+ expect(agent.model).toBe(model)
352
+ expect(isAgentIdentity(agent)).toBe(true)
353
+ }
354
+ })
355
+ })
356
+ })
@@ -0,0 +1,337 @@
1
+ /**
2
+ * Credential Tests - RED PHASE
3
+ *
4
+ * Tests for the Credential type used for authentication credentials.
5
+ * These tests WILL FAIL until the implementation is complete in GREEN phase.
6
+ */
7
+
8
+ import { describe, it, expect } from 'vitest'
9
+ import {
10
+ Credential,
11
+ CredentialSchema,
12
+ isCredential,
13
+ createCredential,
14
+ CredentialType,
15
+ } from '../src'
16
+
17
+ describe('Credential', () => {
18
+ describe('interface shape', () => {
19
+ it('should have required $id field', () => {
20
+ const credential: Credential = {
21
+ $id: 'https://example.com/credentials/1',
22
+ $type: 'https://schema.org.ai/Credential',
23
+ identityId: 'https://example.com/users/1',
24
+ credentialType: 'password',
25
+ }
26
+ expect(credential.$id).toBe('https://example.com/credentials/1')
27
+ })
28
+
29
+ it('should have required $type field with correct value', () => {
30
+ const credential: Credential = {
31
+ $id: 'https://example.com/credentials/1',
32
+ $type: 'https://schema.org.ai/Credential',
33
+ identityId: 'https://example.com/users/1',
34
+ credentialType: 'password',
35
+ }
36
+ expect(credential.$type).toBe('https://schema.org.ai/Credential')
37
+ })
38
+
39
+ it('should have required identityId field', () => {
40
+ const credential: Credential = {
41
+ $id: 'https://example.com/credentials/1',
42
+ $type: 'https://schema.org.ai/Credential',
43
+ identityId: 'https://example.com/users/1',
44
+ credentialType: 'password',
45
+ }
46
+ expect(credential.identityId).toBe('https://example.com/users/1')
47
+ })
48
+
49
+ it('should have required credentialType field', () => {
50
+ const credential: Credential = {
51
+ $id: 'https://example.com/credentials/1',
52
+ $type: 'https://schema.org.ai/Credential',
53
+ identityId: 'https://example.com/users/1',
54
+ credentialType: 'oauth',
55
+ }
56
+ expect(credential.credentialType).toBe('oauth')
57
+ })
58
+
59
+ it('should support optional provider field', () => {
60
+ const credential: Credential = {
61
+ $id: 'https://example.com/credentials/1',
62
+ $type: 'https://schema.org.ai/Credential',
63
+ identityId: 'https://example.com/users/1',
64
+ credentialType: 'oauth',
65
+ provider: 'google',
66
+ }
67
+ expect(credential.provider).toBe('google')
68
+ })
69
+
70
+ it('should support optional expiresAt field', () => {
71
+ const expiresAt = new Date(Date.now() + 3600000).toISOString()
72
+ const credential: Credential = {
73
+ $id: 'https://example.com/credentials/1',
74
+ $type: 'https://schema.org.ai/Credential',
75
+ identityId: 'https://example.com/users/1',
76
+ credentialType: 'api_key',
77
+ expiresAt,
78
+ }
79
+ expect(credential.expiresAt).toBe(expiresAt)
80
+ })
81
+ })
82
+
83
+ describe('CredentialType enum', () => {
84
+ it('should include password type', () => {
85
+ const credential: Credential = {
86
+ $id: 'https://example.com/credentials/1',
87
+ $type: 'https://schema.org.ai/Credential',
88
+ identityId: 'https://example.com/users/1',
89
+ credentialType: 'password',
90
+ }
91
+ expect(credential.credentialType).toBe('password')
92
+ })
93
+
94
+ it('should include oauth type', () => {
95
+ const credential: Credential = {
96
+ $id: 'https://example.com/credentials/1',
97
+ $type: 'https://schema.org.ai/Credential',
98
+ identityId: 'https://example.com/users/1',
99
+ credentialType: 'oauth',
100
+ provider: 'github',
101
+ }
102
+ expect(credential.credentialType).toBe('oauth')
103
+ })
104
+
105
+ it('should include api_key type', () => {
106
+ const credential: Credential = {
107
+ $id: 'https://example.com/credentials/1',
108
+ $type: 'https://schema.org.ai/Credential',
109
+ identityId: 'https://example.com/agents/1',
110
+ credentialType: 'api_key',
111
+ }
112
+ expect(credential.credentialType).toBe('api_key')
113
+ })
114
+
115
+ it('should include sso type', () => {
116
+ const credential: Credential = {
117
+ $id: 'https://example.com/credentials/1',
118
+ $type: 'https://schema.org.ai/Credential',
119
+ identityId: 'https://example.com/users/1',
120
+ credentialType: 'sso',
121
+ provider: 'okta',
122
+ }
123
+ expect(credential.credentialType).toBe('sso')
124
+ })
125
+ })
126
+
127
+ describe('Zod schema validation', () => {
128
+ it('should validate a valid Credential object', () => {
129
+ const result = CredentialSchema.safeParse({
130
+ $id: 'https://example.com/credentials/1',
131
+ $type: 'https://schema.org.ai/Credential',
132
+ identityId: 'https://example.com/users/1',
133
+ credentialType: 'password',
134
+ })
135
+ expect(result.success).toBe(true)
136
+ })
137
+
138
+ it('should validate Credential with all optional fields', () => {
139
+ const result = CredentialSchema.safeParse({
140
+ $id: 'https://example.com/credentials/1',
141
+ $type: 'https://schema.org.ai/Credential',
142
+ identityId: 'https://example.com/users/1',
143
+ credentialType: 'oauth',
144
+ provider: 'google',
145
+ expiresAt: new Date(Date.now() + 3600000).toISOString(),
146
+ })
147
+ expect(result.success).toBe(true)
148
+ })
149
+
150
+ it('should reject Credential without $id', () => {
151
+ const result = CredentialSchema.safeParse({
152
+ $type: 'https://schema.org.ai/Credential',
153
+ identityId: 'https://example.com/users/1',
154
+ credentialType: 'password',
155
+ })
156
+ expect(result.success).toBe(false)
157
+ })
158
+
159
+ it('should reject Credential without identityId', () => {
160
+ const result = CredentialSchema.safeParse({
161
+ $id: 'https://example.com/credentials/1',
162
+ $type: 'https://schema.org.ai/Credential',
163
+ credentialType: 'password',
164
+ })
165
+ expect(result.success).toBe(false)
166
+ })
167
+
168
+ it('should reject Credential without credentialType', () => {
169
+ const result = CredentialSchema.safeParse({
170
+ $id: 'https://example.com/credentials/1',
171
+ $type: 'https://schema.org.ai/Credential',
172
+ identityId: 'https://example.com/users/1',
173
+ })
174
+ expect(result.success).toBe(false)
175
+ })
176
+
177
+ it('should reject Credential with invalid credentialType', () => {
178
+ const result = CredentialSchema.safeParse({
179
+ $id: 'https://example.com/credentials/1',
180
+ $type: 'https://schema.org.ai/Credential',
181
+ identityId: 'https://example.com/users/1',
182
+ credentialType: 'invalid_type',
183
+ })
184
+ expect(result.success).toBe(false)
185
+ })
186
+
187
+ it('should reject Credential with wrong $type', () => {
188
+ const result = CredentialSchema.safeParse({
189
+ $id: 'https://example.com/credentials/1',
190
+ $type: 'https://schema.org.ai/Session',
191
+ identityId: 'https://example.com/users/1',
192
+ credentialType: 'password',
193
+ })
194
+ expect(result.success).toBe(false)
195
+ })
196
+
197
+ it('should reject completely invalid data', () => {
198
+ const result = CredentialSchema.safeParse({ invalid: 'data' })
199
+ expect(result.success).toBe(false)
200
+ })
201
+
202
+ it('should reject non-object values', () => {
203
+ expect(CredentialSchema.safeParse(null).success).toBe(false)
204
+ expect(CredentialSchema.safeParse(undefined).success).toBe(false)
205
+ expect(CredentialSchema.safeParse('string').success).toBe(false)
206
+ expect(CredentialSchema.safeParse(123).success).toBe(false)
207
+ })
208
+ })
209
+
210
+ describe('isCredential type guard', () => {
211
+ it('should return true for valid Credential', () => {
212
+ const credential = {
213
+ $id: 'https://example.com/credentials/1',
214
+ $type: 'https://schema.org.ai/Credential',
215
+ identityId: 'https://example.com/users/1',
216
+ credentialType: 'password',
217
+ }
218
+ expect(isCredential(credential)).toBe(true)
219
+ })
220
+
221
+ it('should return true for Credential with provider', () => {
222
+ const credential = {
223
+ $id: 'https://example.com/credentials/1',
224
+ $type: 'https://schema.org.ai/Credential',
225
+ identityId: 'https://example.com/users/1',
226
+ credentialType: 'oauth',
227
+ provider: 'github',
228
+ }
229
+ expect(isCredential(credential)).toBe(true)
230
+ })
231
+
232
+ it('should return true for Credential with expiresAt', () => {
233
+ const credential = {
234
+ $id: 'https://example.com/credentials/1',
235
+ $type: 'https://schema.org.ai/Credential',
236
+ identityId: 'https://example.com/users/1',
237
+ credentialType: 'api_key',
238
+ expiresAt: new Date(Date.now() + 3600000).toISOString(),
239
+ }
240
+ expect(isCredential(credential)).toBe(true)
241
+ })
242
+
243
+ it('should return false for invalid data', () => {
244
+ expect(isCredential({ invalid: 'data' })).toBe(false)
245
+ })
246
+
247
+ it('should return false for null', () => {
248
+ expect(isCredential(null)).toBe(false)
249
+ })
250
+
251
+ it('should return false for undefined', () => {
252
+ expect(isCredential(undefined)).toBe(false)
253
+ })
254
+
255
+ it('should return false for Session type', () => {
256
+ const session = {
257
+ $id: 'https://example.com/sessions/1',
258
+ $type: 'https://schema.org.ai/Session',
259
+ identityId: 'https://example.com/users/1',
260
+ token: 'abc123',
261
+ expiresAt: new Date(Date.now() + 3600000).toISOString(),
262
+ }
263
+ expect(isCredential(session)).toBe(false)
264
+ })
265
+ })
266
+
267
+ describe('createCredential factory', () => {
268
+ it('should create a valid Credential with required fields', () => {
269
+ const credential = createCredential({
270
+ identityId: 'https://example.com/users/1',
271
+ credentialType: 'password',
272
+ })
273
+ expect(credential.$type).toBe('https://schema.org.ai/Credential')
274
+ expect(credential.identityId).toBe('https://example.com/users/1')
275
+ expect(credential.credentialType).toBe('password')
276
+ })
277
+
278
+ it('should auto-generate $id', () => {
279
+ const credential = createCredential({
280
+ identityId: 'https://example.com/users/1',
281
+ credentialType: 'password',
282
+ })
283
+ expect(credential.$id).toBeDefined()
284
+ expect(credential.$id).toMatch(/^https:\/\//)
285
+ })
286
+
287
+ it('should allow custom $id override', () => {
288
+ const customId = 'https://custom.com/credentials/custom-1'
289
+ const credential = createCredential({
290
+ $id: customId,
291
+ identityId: 'https://example.com/users/1',
292
+ credentialType: 'password',
293
+ })
294
+ expect(credential.$id).toBe(customId)
295
+ })
296
+
297
+ it('should accept optional provider', () => {
298
+ const credential = createCredential({
299
+ identityId: 'https://example.com/users/1',
300
+ credentialType: 'oauth',
301
+ provider: 'google',
302
+ })
303
+ expect(credential.provider).toBe('google')
304
+ })
305
+
306
+ it('should accept optional expiresAt', () => {
307
+ const expiresAt = new Date(Date.now() + 3600000).toISOString()
308
+ const credential = createCredential({
309
+ identityId: 'https://example.com/users/1',
310
+ credentialType: 'api_key',
311
+ expiresAt,
312
+ })
313
+ expect(credential.expiresAt).toBe(expiresAt)
314
+ })
315
+
316
+ it('should create credentials for all credential types', () => {
317
+ const types: CredentialType[] = ['password', 'oauth', 'api_key', 'sso']
318
+ for (const credType of types) {
319
+ const credential = createCredential({
320
+ identityId: 'https://example.com/users/1',
321
+ credentialType: credType,
322
+ })
323
+ expect(credential.credentialType).toBe(credType)
324
+ expect(isCredential(credential)).toBe(true)
325
+ }
326
+ })
327
+
328
+ it('should pass validation after creation', () => {
329
+ const credential = createCredential({
330
+ identityId: 'https://example.com/users/1',
331
+ credentialType: 'password',
332
+ })
333
+ expect(isCredential(credential)).toBe(true)
334
+ expect(CredentialSchema.safeParse(credential).success).toBe(true)
335
+ })
336
+ })
337
+ })