Skip to content

Commit

Permalink
Add tests for null relationship input (#5016)
Browse files Browse the repository at this point in the history
  • Loading branch information
timleslie authored Mar 4, 2021
1 parent 44e7a28 commit a16d2cb
Show file tree
Hide file tree
Showing 12 changed files with 501 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .changeset/three-bikes-speak.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@keystone-next/keystone-legacy': patch
'@keystone-next/api-tests-legacy': patch
---

Added explicit handling of `null` values for relationship fields in `create` and `update` mutations.
2 changes: 2 additions & 0 deletions packages/keystone/lib/ListTypes/list.js
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,8 @@ module.exports = class List {
async _resolveRelationship(data, existingItem, context, getItem, mutationState) {
const fields = this._fieldsFromObject(data).filter(field => field.isRelationship);
const resolvedRelationships = await mapToFields(fields, async field => {
// Treat `null` as `undefined`, e.g. a no-op
if (data[field.path] === null) return undefined;
const { create, connect, disconnect, currentValue } = await field.resolveNestedOperations(
data[field.path],
existingItem,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,25 @@ multiAdapterRunners().map(({ runner, adapterName }) =>
expect(User.friends.map(({ id }) => id.toString())).toEqual([Friend.id.toString()]);
})
);

test(
'With null',
runner(setupKeystone, async ({ context }) => {
const { data, errors } = await context.executeGraphQL({
query: `
mutation {
createUser(data: {
friends: null
}) { id friends { id } }
}
`,
});
expect(errors).toBe(undefined);

// Friends should be empty
expect(data.createUser.friends).toHaveLength(0);
})
);
});

describe('Update', () => {
Expand Down Expand Up @@ -347,6 +366,32 @@ multiAdapterRunners().map(({ runner, adapterName }) =>
expect(result.User.friends).toEqual([]);
})
);

test(
'With null',
runner(setupKeystone, async ({ context }) => {
// Manually setup a connected Company <-> Location
const { user, friend } = await createUserAndFriend(context);

// Run the query with a null operation
const { data, errors } = await context.executeGraphQL({
query: `
mutation {
updateUser(
id: "${user.id}",
data: { friends: null }
) { id friends { id name } }
}
`,
});
expect(errors).toBe(undefined);

// Check that the friends are still there
expect(data.updateUser.id).toEqual(user.id);
expect(data.updateUser.friends).toHaveLength(1);
expect(data.updateUser.friends[0].id).toEqual(friend.id);
})
);
});

describe('Delete', () => {
Expand Down
45 changes: 45 additions & 0 deletions tests/api-tests/relationships/crud-self-ref/many-to-many.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,25 @@ multiAdapterRunners().map(({ runner, adapterName }) =>
]);
})
);

test(
'With null',
runner(setupKeystone, async ({ context }) => {
const { data, errors } = await context.executeGraphQL({
query: `
mutation {
createUser(data: {
friends: null
}) { id friends { id } }
}
`,
});
expect(errors).toBe(undefined);

// Friends should be empty
expect(data.createUser.friends).toHaveLength(0);
})
);
});

describe('Update', () => {
Expand Down Expand Up @@ -451,6 +470,32 @@ multiAdapterRunners().map(({ runner, adapterName }) =>
expect(result.Friend.friendOf).toEqual([]);
})
);

test(
'With null',
runner(setupKeystone, async ({ context }) => {
// Manually setup a connected Company <-> Location
const { user, friend } = await createUserAndFriend(context);

// Run the query with a null operation
const { data, errors } = await context.executeGraphQL({
query: `
mutation {
updateUser(
id: "${user.id}",
data: { friends: null }
) { id friends { id name } }
}
`,
});
expect(errors).toBe(undefined);

// Check that the friends are still there
expect(data.updateUser.id).toEqual(user.id);
expect(data.updateUser.friends).toHaveLength(1);
expect(data.updateUser.friends[0].id).toEqual(friend.id);
})
);
});

describe('Delete', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,25 @@ multiAdapterRunners().map(({ runner, adapterName }) =>
expect(User.friend.id.toString()).toBe(Friend.id.toString());
})
);

test(
'With null',
runner(setupKeystone, async ({ context }) => {
const { data, errors } = await context.executeGraphQL({
query: `
mutation {
createUser(data: {
friend: null
}) { id friend { id } }
}
`,
});
expect(errors).toBe(undefined);

// Friend should be empty
expect(data.createUser.friend).toBe(null);
})
);
});

describe('Update', () => {
Expand Down Expand Up @@ -339,6 +358,32 @@ multiAdapterRunners().map(({ runner, adapterName }) =>
expect(result.User.friend).toBe(null);
})
);

test(
'With null',
runner(setupKeystone, async ({ context }) => {
// Manually setup a connected Company <-> Location
const { friend, user } = await createUserAndFriend(context);

// Run the query with a null operation
const { data, errors } = await context.executeGraphQL({
query: `
mutation {
updateUser(
id: "${user.id}",
data: { friend: null }
) { id friend { id name } }
}
`,
});
expect(errors).toBe(undefined);

// Check that the friend is still there
expect(data.updateUser.id).toEqual(user.id);
expect(data.updateUser.friend).not.toBe(null);
expect(data.updateUser.friend.id).toEqual(friend.id);
})
);
});

describe('Delete', () => {
Expand Down
45 changes: 45 additions & 0 deletions tests/api-tests/relationships/crud-self-ref/one-to-many.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,25 @@ multiAdapterRunners().map(({ runner, adapterName }) =>
});
})
);

test(
'With null',
runner(setupKeystone, async ({ context }) => {
const { data, errors } = await context.executeGraphQL({
query: `
mutation {
createUser(data: {
friends: null
}) { id friends { id } }
}
`,
});
expect(errors).toBe(undefined);

// Friends should be empty
expect(data.createUser.friends).toHaveLength(0);
})
);
});

describe('Update', () => {
Expand Down Expand Up @@ -487,6 +506,32 @@ multiAdapterRunners().map(({ runner, adapterName }) =>
expect(result.Friend.friendOf).toBe(null);
})
);

test(
'With null',
runner(setupKeystone, async ({ context }) => {
// Manually setup a connected Company <-> Location
const { user, friend } = await createUserAndFriend(context);

// Run the query with a null operation
const { data, errors } = await context.executeGraphQL({
query: `
mutation {
updateUser(
id: "${user.id}",
data: { friends: null }
) { id friends { id name } }
}
`,
});
expect(errors).toBe(undefined);

// Check that the friends are still there
expect(data.updateUser.id).toEqual(user.id);
expect(data.updateUser.friends).toHaveLength(1);
expect(data.updateUser.friends[0].id).toEqual(friend.id);
})
);
});

describe('Delete', () => {
Expand Down
45 changes: 45 additions & 0 deletions tests/api-tests/relationships/crud-self-ref/one-to-one.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,25 @@ multiAdapterRunners().map(({ runner, adapterName }) =>
});
})
);

test(
'With null',
runner(setupKeystone, async ({ context }) => {
const { data, errors } = await context.executeGraphQL({
query: `
mutation {
createUser(data: {
friend: null
}) { id friend { id } }
}
`,
});
expect(errors).toBe(undefined);

// Friend should be empty
expect(data.createUser.friend).toBe(null);
})
);
});

describe('Update', () => {
Expand Down Expand Up @@ -501,6 +520,32 @@ multiAdapterRunners().map(({ runner, adapterName }) =>
expect(result.Friend.friendOf).toBe(null);
})
);

test(
'With null',
runner(setupKeystone, async ({ context }) => {
// Manually setup a connected Company <-> Location
const { user, friend } = await createUserAndFriend(context);

// Run the query with a null operation
const { data, errors } = await context.executeGraphQL({
query: `
mutation {
updateUser(
id: "${user.id}",
data: { friend: null }
) { id friend { id name } }
}
`,
});
expect(errors).toBe(undefined);

// Check that the friend is still there
expect(data.updateUser.id).toEqual(user.id);
expect(data.updateUser.friend).not.toBe(null);
expect(data.updateUser.friend.id).toEqual(friend.id);
})
);
});

describe('Delete', () => {
Expand Down
45 changes: 45 additions & 0 deletions tests/api-tests/relationships/crud/many-to-many-one-sided.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,25 @@ multiAdapterRunners().map(({ runner, adapterName }) =>
]);
})
);

test(
'With null',
runner(setupKeystone, async ({ context }) => {
const { data, errors } = await context.executeGraphQL({
query: `
mutation {
createCompany(data: {
locations: null
}) { id locations { id } }
}
`,
});
expect(errors).toBe(undefined);

// Locations should be empty
expect(data.createCompany.locations).toHaveLength(0);
})
);
});

describe('Update', () => {
Expand Down Expand Up @@ -443,6 +462,32 @@ multiAdapterRunners().map(({ runner, adapterName }) =>
expect(result.Company.locations).toEqual([]);
})
);

test(
'With null',
runner(setupKeystone, async ({ context }) => {
// Manually setup a connected Company <-> Location
const { location, company } = await createCompanyAndLocation(context);

// Run the query with a null operation
const { data, errors } = await context.executeGraphQL({
query: `
mutation {
updateCompany(
id: "${company.id}",
data: { locations: null }
) { id locations { id name } }
}
`,
});
expect(errors).toBe(undefined);

// Check that the locations are still there
expect(data.updateCompany.id).toEqual(company.id);
expect(data.updateCompany.locations).toHaveLength(1);
expect(data.updateCompany.locations[0].id).toEqual(location.id);
})
);
});

describe('Delete', () => {
Expand Down
Loading

1 comment on commit a16d2cb

@vercel
Copy link

@vercel vercel bot commented on a16d2cb Mar 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.