Skip to content

Commit 7537320

Browse files
committed
test: add two-way unique one-to-one relations tests
1 parent 0b4c441 commit 7537320

File tree

3 files changed

+172
-6
lines changed

3 files changed

+172
-6
lines changed

src/collection.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,9 @@ export class Collection<Schema extends StandardSchemaV1> {
131131

132132
if (validationResult.issues) {
133133
console.error(validationResult.issues)
134+
134135
throw new OperationError(
135-
'Failed to create a new record with initial values (%j): does not match the schema. Please see the schema validation errors above.',
136+
'Failed to create a new record with initial values: does not match the schema. Please see the schema validation errors above.',
136137
OperationErrorCodes.INVALID_INITIAL_VALUES,
137138
)
138139
}

src/relation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ export abstract class Relation {
359359
this.#createErrorDetails(),
360360
),
361361
isUnique,
362-
`Failed to create a unique relation at "%s": foreign ${this instanceof Many ? 'records' : 'record'} already associated with another owner`,
362+
`Failed to create a unique relation at "%s": the foreign ${this instanceof Many ? 'records' : 'record'} is already associated with another owner`,
363363
serializedPath,
364364
)
365365

tests/relations/one-to-one.test.ts

Lines changed: 169 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ it('applies relation to records created before the relation is defined', async (
241241
})
242242
})
243243

244-
it('supports creating unique one-to-one relations', async () => {
244+
it('supports creating unique one-way one-to-one relations', async () => {
245245
const userSchema = z.object({ id: z.number() })
246246
const postSchema = z.object({
247247
title: z.string(),
@@ -290,7 +290,7 @@ it('supports creating unique one-to-one relations', async () => {
290290
})
291291
})
292292

293-
it('supports updating unique one-to-one relations', async () => {
293+
it('supports updating unique one-way one-to-one relations', async () => {
294294
const userSchema = z.object({ id: z.number() })
295295
const postSchema = z.object({
296296
title: z.string(),
@@ -328,7 +328,7 @@ it('supports updating unique one-to-one relations', async () => {
328328
})
329329
})
330330

331-
it('errors when creating a unique relation with a foreign record that has already been associated', async () => {
331+
it('errors when creating a unique one-way relation referencing a taken foreign record', async () => {
332332
const userSchema = z.object({ id: z.number() })
333333
const postSchema = z.object({
334334
title: z.string(),
@@ -360,7 +360,7 @@ it('errors when creating a unique relation with a foreign record that has alread
360360
)
361361
})
362362

363-
it('errors when updating a unique relation with a foreign record that has already been associated', async () => {
363+
it('errors when updating a unique one-way relation referencing a taken foreign record', async () => {
364364
const userSchema = z.object({ id: z.number() })
365365
const postSchema = z.object({
366366
title: z.string(),
@@ -398,3 +398,168 @@ it('errors when updating a unique relation with a foreign record that has alread
398398
),
399399
)
400400
})
401+
402+
it('supports creating unique two-way one-to-one relations', async () => {
403+
const userSchema = z.object({
404+
id: z.number(),
405+
get favoritePost() {
406+
return postSchema
407+
},
408+
})
409+
const postSchema = z.object({
410+
title: z.string(),
411+
get author() {
412+
return userSchema.optional()
413+
},
414+
})
415+
416+
const users = new Collection({ schema: userSchema })
417+
const posts = new Collection({ schema: postSchema })
418+
419+
users.defineRelations(({ one }) => ({
420+
favoritePost: one(posts, { unique: true }),
421+
}))
422+
posts.defineRelations(({ one }) => ({
423+
author: one(users, { unique: true }),
424+
}))
425+
426+
const user = await users.create({
427+
id: 1,
428+
favoritePost: await posts.create({ title: 'First' }),
429+
})
430+
expect(user.favoritePost).toEqual({ title: 'First', author: user })
431+
expect(posts.findFirst((q) => q.where({ author: { id: 1 } }))).toEqual({
432+
title: 'First',
433+
author: user,
434+
})
435+
})
436+
437+
it('errors when creating a unique two-way relation referencing a taken foreign record', async () => {
438+
const userSchema = z.object({
439+
id: z.number(),
440+
get favoritePost() {
441+
return postSchema.optional()
442+
},
443+
})
444+
const postSchema = z.object({
445+
title: z.string(),
446+
get author() {
447+
return userSchema.optional()
448+
},
449+
})
450+
451+
const users = new Collection({ schema: userSchema })
452+
const posts = new Collection({ schema: postSchema })
453+
454+
users.defineRelations(({ one }) => ({
455+
favoritePost: one(posts, { unique: true }),
456+
}))
457+
posts.defineRelations(({ one }) => ({
458+
author: one(users, { unique: true }),
459+
}))
460+
461+
const user = await users.create({
462+
id: 1,
463+
favoritePost: await posts.create({ title: 'First' }),
464+
})
465+
466+
await expect(
467+
users.create({ id: 2, favoritePost: user.favoritePost }),
468+
).rejects.toThrow(
469+
new RelationError(
470+
`Failed to create a unique relation at "favoritePost": the foreign record is already associated with another owner`,
471+
RelationErrorCodes.FORBIDDEN_UNIQUE_CREATE,
472+
{
473+
path: ['favoritePost'],
474+
ownerCollection: users,
475+
foreignCollections: [posts],
476+
options: { unique: true },
477+
},
478+
),
479+
)
480+
481+
await expect(posts.create({ title: 'Second', author: user })).rejects.toThrow(
482+
new RelationError(
483+
`Failed to create a unique relation at "author": the foreign record is already associated with another owner`,
484+
RelationErrorCodes.FORBIDDEN_UNIQUE_CREATE,
485+
{
486+
path: ['author'],
487+
ownerCollection: posts,
488+
foreignCollections: [users],
489+
options: { unique: true },
490+
},
491+
),
492+
)
493+
})
494+
495+
it('errors when updating a unique two-way relation referencing a taken foreign record', async () => {
496+
const userSchema = z.object({
497+
id: z.number(),
498+
get favoritePost() {
499+
return postSchema
500+
},
501+
})
502+
const postSchema = z.object({
503+
title: z.string(),
504+
get author() {
505+
return userSchema.optional()
506+
},
507+
})
508+
509+
const users = new Collection({ schema: userSchema })
510+
const posts = new Collection({ schema: postSchema })
511+
512+
users.defineRelations(({ one }) => ({
513+
favoritePost: one(posts, { unique: true }),
514+
}))
515+
posts.defineRelations(({ one }) => ({
516+
author: one(users, { unique: true }),
517+
}))
518+
519+
const firstUser = await users.create({
520+
id: 1,
521+
favoritePost: await posts.create({ title: 'First' }),
522+
})
523+
const secondUser = await users.create({
524+
id: 2,
525+
favoritePost: await posts.create({ title: 'Second' }),
526+
})
527+
528+
await expect(
529+
users.update(secondUser, {
530+
data(user) {
531+
user.favoritePost = firstUser.favoritePost
532+
},
533+
}),
534+
).rejects.toThrow(
535+
new RelationError(
536+
`Failed to update a unique relation at "favoritePost": the foreign record is already associated with another owner`,
537+
RelationErrorCodes.FORBIDDEN_UNIQUE_UPDATE,
538+
{
539+
path: ['favoritePost'],
540+
ownerCollection: users,
541+
foreignCollections: [posts],
542+
options: { unique: true },
543+
},
544+
),
545+
)
546+
547+
await expect(
548+
posts.update((q) => q.where({ author: { id: 2 } }), {
549+
data(post) {
550+
post.author = firstUser
551+
},
552+
}),
553+
).rejects.toThrow(
554+
new RelationError(
555+
`Failed to update a unique relation at "author": the foreign record is already associated with another owner`,
556+
RelationErrorCodes.FORBIDDEN_UNIQUE_UPDATE,
557+
{
558+
path: ['author'],
559+
ownerCollection: posts,
560+
foreignCollections: [users],
561+
options: { unique: true },
562+
},
563+
),
564+
)
565+
})

0 commit comments

Comments
 (0)