console/integration-tests/tests/cli/schema.spec.ts
renovate[bot] 1afe0ec73a
Update dependency @theguild/prettier-config to v1 (#676)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Kamil Kisiela <kamil.kisiela@gmail.com>
2022-11-24 10:00:41 +00:00

628 lines
18 KiB
TypeScript

import { TargetAccessScope, ProjectType } from '@app/gql/graphql';
import { createHash } from 'node:crypto';
import { schemaPublish, schemaCheck } from '../../testkit/cli';
import { authenticate } from '../../testkit/auth';
import {
createOrganization,
joinOrganization,
createProject,
createToken,
fetchSupergraphFromCDN,
inviteToOrganization,
} from '../../testkit/flow';
test('can publish and check a schema with target:registry:read access', async () => {
const { access_token: owner_access_token } = await authenticate('main');
const orgResult = await createOrganization(
{
name: 'foo',
},
owner_access_token,
);
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
const invitationResult = await inviteToOrganization(
{
email: 'some@email.com',
organization: org.cleanId,
},
owner_access_token,
);
const inviteCode = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
expect(inviteCode).toBeDefined();
// Join
const { access_token: member_access_token } = await authenticate('extra');
await joinOrganization(inviteCode!, member_access_token);
const projectResult = await createProject(
{
organization: org.cleanId,
type: ProjectType.Single,
name: 'foo',
},
owner_access_token,
);
const project = projectResult.body.data!.createProject.ok!.createdProject;
const target = projectResult.body.data!.createProject.ok!.createdTargets[0];
// Create a token with write rights
const writeTokenResult = await createToken(
{
name: 'test',
organization: org.cleanId,
project: project.cleanId,
target: target.cleanId,
organizationScopes: [],
projectScopes: [],
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
},
owner_access_token,
);
expect(writeTokenResult.body.errors).not.toBeDefined();
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
await schemaPublish([
'--token',
writeToken,
'--author',
'Kamil',
'--commit',
'abc123',
'fixtures/init-schema.graphql',
]);
await schemaCheck(['--token', writeToken, 'fixtures/nonbreaking-schema.graphql']);
await expect(
schemaCheck(['--token', writeToken, 'fixtures/breaking-schema.graphql']),
).rejects.toThrowError(/breaking/);
});
test('publishing a breaking change results in invalid state', async () => {
const { access_token: owner_access_token } = await authenticate('main');
const orgResult = await createOrganization(
{
name: 'foo',
},
owner_access_token,
);
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
const invitationResult = await inviteToOrganization(
{
email: 'some@email.com',
organization: org.cleanId,
},
owner_access_token,
);
const inviteCode = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
expect(inviteCode).toBeDefined();
// Join
const { access_token: member_access_token } = await authenticate('extra');
await joinOrganization(inviteCode!, member_access_token);
const projectResult = await createProject(
{
organization: org.cleanId,
type: ProjectType.Single,
name: 'foo',
},
owner_access_token,
);
const project = projectResult.body.data!.createProject.ok!.createdProject;
const target = projectResult.body.data!.createProject.ok!.createdTargets[0];
// Create a token with write rights
const writeTokenResult = await createToken(
{
name: 'test',
organization: org.cleanId,
project: project.cleanId,
target: target.cleanId,
organizationScopes: [],
projectScopes: [],
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
},
owner_access_token,
);
expect(writeTokenResult.body.errors).not.toBeDefined();
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
await schemaPublish([
'--token',
writeToken,
'--author',
'Kamil',
'--commit',
'abc123',
'fixtures/init-schema.graphql',
]);
await expect(
schemaPublish(['--token', writeToken, 'fixtures/breaking-schema.graphql']),
).rejects.toThrowError(/breaking/);
});
test('publishing invalid schema SDL provides meaningful feedback for the user.', async () => {
const { access_token: owner_access_token } = await authenticate('main');
const orgResult = await createOrganization(
{
name: 'foo',
},
owner_access_token,
);
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
const invitationResult = await inviteToOrganization(
{
email: 'some@email.com',
organization: org.cleanId,
},
owner_access_token,
);
const code = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
expect(code).toBeDefined();
// Join
const { access_token: member_access_token } = await authenticate('extra');
await joinOrganization(code!, member_access_token);
const projectResult = await createProject(
{
organization: org.cleanId,
type: ProjectType.Single,
name: 'foo',
},
owner_access_token,
);
const project = projectResult.body.data!.createProject.ok!.createdProject;
const target = projectResult.body.data!.createProject.ok!.createdTargets[0];
// Create a token with write rights
const writeTokenResult = await createToken(
{
name: 'test',
organization: org.cleanId,
project: project.cleanId,
target: target.cleanId,
organizationScopes: [],
projectScopes: [],
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
},
owner_access_token,
);
expect(writeTokenResult.body.errors).not.toBeDefined();
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
const allocatedError = new Error('Should have thrown.');
try {
await schemaPublish([
'--token',
writeToken,
'--author',
'Kamil',
'--commit',
'abc123',
'fixtures/init-invalid-schema.graphql',
]);
throw allocatedError;
} catch (err) {
if (err === allocatedError) {
throw err;
}
expect(String(err)).toMatch(`The SDL is not valid at line 1, column 1:`);
expect(String(err)).toMatch(`Syntax Error: Unexpected Name "iliketurtles"`);
}
});
test('service url should be available in supergraph', async () => {
const { access_token: owner_access_token } = await authenticate('main');
const orgResult = await createOrganization(
{
name: 'foo',
},
owner_access_token,
);
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
const invitationResult = await inviteToOrganization(
{
email: 'some@email.com',
organization: org.cleanId,
},
owner_access_token,
);
const code = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
expect(code).toBeDefined();
// Join
const { access_token: member_access_token } = await authenticate('extra');
await joinOrganization(code!, member_access_token);
const projectResult = await createProject(
{
organization: org.cleanId,
type: ProjectType.Federation,
name: 'foo',
},
owner_access_token,
);
const project = projectResult.body.data!.createProject.ok!.createdProject;
const target = projectResult.body.data!.createProject.ok!.createdTargets[0];
// Create a token with write rights
const writeTokenResult = await createToken(
{
name: 'test',
organization: org.cleanId,
project: project.cleanId,
target: target.cleanId,
organizationScopes: [],
projectScopes: [],
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
},
owner_access_token,
);
expect(writeTokenResult.body.errors).not.toBeDefined();
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
await schemaPublish([
'--token',
writeToken,
'--author',
'Kamil',
'--commit',
'abc123',
'--service',
'users',
'--url',
'https://api.com/users-subgraph',
'fixtures/federation-init.graphql',
]);
const supergraph = await fetchSupergraphFromCDN(
{
organization: org.cleanId,
project: project.cleanId,
target: target.cleanId,
},
writeToken,
);
expect(supergraph.body).toMatch('(name: "users" url: "https://api.com/users-subgraph")');
});
test('service url should be required in Federation', async () => {
const { access_token: owner_access_token } = await authenticate('main');
const orgResult = await createOrganization(
{
name: 'foo',
},
owner_access_token,
);
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
const invitationResult = await inviteToOrganization(
{
email: 'some@email.com',
organization: org.cleanId,
},
owner_access_token,
);
const code = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
expect(code).toBeDefined();
// Join
const { access_token: member_access_token } = await authenticate('extra');
await joinOrganization(code!, member_access_token);
const projectResult = await createProject(
{
organization: org.cleanId,
type: ProjectType.Federation,
name: 'foo',
},
owner_access_token,
);
const project = projectResult.body.data!.createProject.ok!.createdProject;
const target = projectResult.body.data!.createProject.ok!.createdTargets[0];
// Create a token with write rights
const writeTokenResult = await createToken(
{
name: 'test',
organization: org.cleanId,
project: project.cleanId,
target: target.cleanId,
organizationScopes: [],
projectScopes: [],
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
},
owner_access_token,
);
expect(writeTokenResult.body.errors).not.toBeDefined();
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
await expect(
schemaPublish([
'--token',
writeToken,
'--author',
'Kamil',
'--commit',
'abc123',
'--service',
'users',
'fixtures/federation-init.graphql',
]),
).rejects.toThrowError(/url/);
});
test('schema:publish should print a link to the website', async () => {
const { access_token: owner_access_token } = await authenticate('main');
const orgResult = await createOrganization(
{
name: 'bar',
},
owner_access_token,
);
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
const invitationResult = await inviteToOrganization(
{
email: 'some@email.com',
organization: org.cleanId,
},
owner_access_token,
);
const code = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
expect(code).toBeDefined();
// Join
const { access_token: member_access_token } = await authenticate('extra');
await joinOrganization(code!, member_access_token);
const projectResult = await createProject(
{
organization: org.cleanId,
type: ProjectType.Single,
name: 'foo',
},
owner_access_token,
);
const project = projectResult.body.data!.createProject.ok!.createdProject;
const targets = projectResult.body.data!.createProject.ok!.createdTargets;
const target = targets.find(t => t.name === 'development')!;
// Create a token with write rights
const writeTokenResult = await createToken(
{
name: 'test',
organization: org.cleanId,
project: project.cleanId,
target: target.cleanId,
organizationScopes: [],
projectScopes: [],
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
},
owner_access_token,
);
expect(writeTokenResult.body.errors).not.toBeDefined();
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
await expect(
schemaPublish(['--token', writeToken, 'fixtures/init-schema.graphql']),
).resolves.toMatch('Available at https://app.graphql-hive.com/bar/foo/development');
await expect(
schemaPublish(['--token', writeToken, 'fixtures/nonbreaking-schema.graphql']),
).resolves.toMatch('Available at https://app.graphql-hive.com/bar/foo/development/history/');
});
test('schema:check should notify user when registry is empty', async () => {
const { access_token: owner_access_token } = await authenticate('main');
const orgResult = await createOrganization(
{
name: 'foo',
},
owner_access_token,
);
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
const invitationResult = await inviteToOrganization(
{
email: 'some@email.com',
organization: org.cleanId,
},
owner_access_token,
);
const code = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
expect(code).toBeDefined();
// Join
const { access_token: member_access_token } = await authenticate('extra');
await joinOrganization(code!, member_access_token);
const projectResult = await createProject(
{
organization: org.cleanId,
type: ProjectType.Single,
name: 'foo',
},
owner_access_token,
);
const project = projectResult.body.data!.createProject.ok!.createdProject;
const target = projectResult.body.data!.createProject.ok!.createdTargets[0];
// Create a token with write rights
const writeTokenResult = await createToken(
{
name: 'test',
organization: org.cleanId,
project: project.cleanId,
target: target.cleanId,
organizationScopes: [],
projectScopes: [],
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
},
owner_access_token,
);
expect(writeTokenResult.body.errors).not.toBeDefined();
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
await expect(
schemaCheck(['--token', writeToken, 'fixtures/init-schema.graphql']),
).resolves.toMatch('empty');
});
test('schema:check should throw on corrupted schema', async () => {
const { access_token: owner_access_token } = await authenticate('main');
const orgResult = await createOrganization(
{
name: 'foo',
},
owner_access_token,
);
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
const invitationResult = await inviteToOrganization(
{
email: 'some@email.com',
organization: org.cleanId,
},
owner_access_token,
);
const code = invitationResult.body.data?.inviteToOrganizationByEmail.ok?.code;
expect(code).toBeDefined();
// Join
const { access_token: member_access_token } = await authenticate('extra');
await joinOrganization(code!, member_access_token);
const projectResult = await createProject(
{
organization: org.cleanId,
type: ProjectType.Single,
name: 'foo',
},
owner_access_token,
);
const project = projectResult.body.data!.createProject.ok!.createdProject;
const target = projectResult.body.data!.createProject.ok!.createdTargets[0];
// Create a token with write rights
const writeTokenResult = await createToken(
{
name: 'test',
organization: org.cleanId,
project: project.cleanId,
target: target.cleanId,
organizationScopes: [],
projectScopes: [],
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
},
owner_access_token,
);
expect(writeTokenResult.body.errors).not.toBeDefined();
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
const output = schemaCheck(['--token', writeToken, 'fixtures/missing-type.graphql']);
await expect(output).rejects.toThrowError('Unknown type');
});
test('schema:publish should see Invalid Token error when token is invalid', async () => {
const { access_token: owner_access_token } = await authenticate('main');
const orgResult = await createOrganization(
{
name: 'foo',
},
owner_access_token,
);
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
await createProject(
{
organization: org.cleanId,
type: ProjectType.Single,
name: 'foo',
},
owner_access_token,
);
const token = createHash('md5').update('nope').digest('hex').substring(0, 31);
const output = schemaPublish(['--token', token, 'fixtures/init-schema.graphql']);
await expect(output).rejects.toThrowError('Invalid token provided');
});
test('schema:publish should support experimental_acceptBreakingChanges flag', async () => {
const { access_token: owner_access_token } = await authenticate('main');
const orgResult = await createOrganization(
{
name: 'bar',
},
owner_access_token,
);
const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;
const projectResult = await createProject(
{
organization: org.cleanId,
type: ProjectType.Single,
name: 'foo',
},
owner_access_token,
);
const project = projectResult.body.data!.createProject.ok!.createdProject;
const targets = projectResult.body.data!.createProject.ok!.createdTargets;
const target = targets.find(t => t.name === 'development')!;
// Create a token with write rights
const writeTokenResult = await createToken(
{
name: 'test',
organization: org.cleanId,
project: project.cleanId,
target: target.cleanId,
organizationScopes: [],
projectScopes: [],
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
},
owner_access_token,
);
expect(writeTokenResult.body.errors).not.toBeDefined();
const writeToken = writeTokenResult.body.data!.createToken.ok!.secret;
await expect(
schemaPublish(['--token', writeToken, 'fixtures/init-schema.graphql']),
).resolves.toMatch('Available at https://app.graphql-hive.com/bar/foo/development');
await expect(
schemaPublish([
'--token',
writeToken,
'--experimental_acceptBreakingChanges',
'fixtures/breaking-schema.graphql',
]),
).resolves.toMatch('Available at https://app.graphql-hive.com/bar/foo/development/history/');
});