import { GraphQLInteraction, Pact, Matchers } from '@pact-foundation/pact'; import { resolve } from 'path'; import { resolveAfter } from '$/utils/resolve_after'; const { eachLike, like } = Matchers; jest.mock('$/config/config.js'); import { getTopic } from './topics'; const internals = { provider: null }; describe('Topics store pact', () => { beforeAll(async () => { internals.provider = new Pact({ port: 1234, dir: resolve(process.cwd(), 'pacts'), consumer: 'ForumClient', provider: 'ForumServer', pactfileWriteMode: 'update' }); await internals.provider.setup(); }); afterEach(() => internals.provider.verify()); afterAll(() => internals.provider.finalize()); describe('When there\'s data', () => { describe('GetTopic', () => { beforeAll(async () => { const topicQuery = new GraphQLInteraction() .given('there\'s data') .uponReceiving('a request to get a single topic') .withRequest({ path: '/graphql', method: 'POST' }) .withOperation('GetTopic') .withQuery( `query GetTopic($id: ID!) { topic(id: $id) { id title updated_at ttl forum { id glyph label __typename } tags { id weight __typename } posts { id text created_at author { id handle __typename } __typename } __typename } }` ) .withVariables({ id: '0b58959d-d448-4a4e-84b6-35e5ac0028d1' }) .willRespondWith({ status: 200, headers: { 'Content-Type': 'application/json; charset=utf-8' }, body: { data: { topic: { id: like('0b58959d-d448-4a4e-84b6-35e5ac0028d1'), title: like('The pacty topic of the day'), updated_at: like(1619979888906), ttl: like(3399), forum: { id: like('cucumber'), glyph: like('✽'), label: like('test_forums.cucumber') }, tags: eachLike({ id: like('skunk'), weight: like(44) }), posts: eachLike({ id: like('ed93530e-6f9c-4701-91ef-14f9e0ed3e26'), text: like('The content of this post is very relevant'), created_at: like(1619979889798), author: like({ id: like('07fb2ba0-0945-464a-b215-873296710c8c'), handle: like('cucumber_fan92') }) }) } } } }); return await internals.provider.addInteraction(topicQuery); }); test('it returns the topic', async () => { const topic = getTopic('0b58959d-d448-4a4e-84b6-35e5ac0028d1'); const { counter, promise: resolveAfterTwo } = resolveAfter(2); let response = null; topic.subscribe((topicValue) => { response = topicValue; counter(); }); expect(response.data).toBe(null); expect(response.loading).toBe(true); expect(response.error).toBe(undefined); await resolveAfterTwo; expect(response.data).toEqual({ id: '0b58959d-d448-4a4e-84b6-35e5ac0028d1', title: 'The pacty topic of the day', updated_at: 1619979888906, ttl: 3399, forum: { id: 'cucumber', glyph: '✽', label: 'test_forums.cucumber' }, tags: [{ id: 'skunk', weight: 44 }], posts: [{ id: 'ed93530e-6f9c-4701-91ef-14f9e0ed3e26', text: 'The content of this post is very relevant', created_at: 1619979889798, author: { id: '07fb2ba0-0945-464a-b215-873296710c8c', handle: 'cucumber_fan92' } }] }); expect(response.loading).toBe(false); expect(response.error).toBe(undefined); }); }); }); describe('When there\'s no data', () => { describe('GetTopic', () => { beforeAll(async () => { const topicQuery = new GraphQLInteraction() .given('there\'s no data') .uponReceiving('a request to get a single topic') .withRequest({ path: '/graphql', method: 'POST' }) .withOperation('GetTopic') .withQuery( `query GetTopic($id: ID!) { topic(id: $id) { id title updated_at ttl forum { id glyph label __typename } tags { id weight __typename } posts { id text created_at author { id handle __typename } __typename } __typename } }` ) .withVariables({ id: '0b58959d-d448-4a4e-84b6-35e5ac0028d1' }) .willRespondWith({ status: 200, headers: { 'Content-Type': 'application/json; charset=utf-8' }, body: { data: { topic: null } } }); return await internals.provider.addInteraction(topicQuery); }); test('it returns the topic', async () => { const topic = getTopic('0b58959d-d448-4a4e-84b6-35e5ac0028d1'); const { counter, promise: resolveAfterTwo } = resolveAfter(2); let response = null; topic.subscribe((topicValue) => { response = topicValue; counter(); }); expect(response.data).toBe(null); expect(response.loading).toBe(true); expect(response.error).toBe(undefined); await resolveAfterTwo; expect(response.data).toBe(null); expect(response.loading).toBe(false); expect(response.error).toBe(undefined); }); }); }); describe('When there\'s a server error', () => { describe('GetTopic', () => { beforeAll(async () => { const topicQuery = new GraphQLInteraction() .given('there\'s a server error') .uponReceiving('a request to get a single topic') .withRequest({ path: '/graphql', method: 'POST' }) .withOperation('GetTopic') .withQuery( `query GetTopic($id: ID!) { topic(id: $id) { id title updated_at ttl forum { id glyph label __typename } tags { id weight __typename } posts { id text created_at author { id handle __typename } __typename } __typename } }` ) .withVariables({ id: '0b58959d-d448-4a4e-84b6-35e5ac0028d1' }) .willRespondWith({ status: 500 }); return await internals.provider.addInteraction(topicQuery); }); test('it returns the error', async () => { const topic = getTopic('0b58959d-d448-4a4e-84b6-35e5ac0028d1'); const { counter, promise: resolveAfterTwo } = resolveAfter(2); let response = null; topic.subscribe((topicValue) => { response = topicValue; counter(); }); expect(response.data).toBe(null); expect(response.loading).toBe(true); expect(response.error).toBe(undefined); await resolveAfterTwo; expect(response.data).toBe(null); expect(response.loading).toBe(false); expect(response.error).toBeInstanceOf(Error); }); }); }); describe('When there\'s an error in the response', () => { describe('GetTopic', () => { beforeAll(async () => { const topicQuery = new GraphQLInteraction() .given('there\'s an error in the response') .uponReceiving('a request to get a single topic') .withRequest({ path: '/graphql', method: 'POST' }) .withOperation('GetTopic') .withQuery( `query GetTopic($id: ID!) { topic(id: $id) { id title updated_at ttl forum { id glyph label __typename } tags { id weight __typename } posts { id text created_at author { id handle __typename } __typename } __typename } }` ) .withVariables({ id: '0b58959d-d448-4a4e-84b6-35e5ac0028d1' }) .willRespondWith({ status: 200, headers: { 'Content-Type': 'application/json; charset=utf-8' }, body: { errors: eachLike({ message: like('An error occurred when fetching the topic') }) } }); return await internals.provider.addInteraction(topicQuery); }); test('it returns the error', async () => { const topic = getTopic('0b58959d-d448-4a4e-84b6-35e5ac0028d1'); const { counter, promise: resolveAfterTwo } = resolveAfter(2); let response = null; topic.subscribe((topicValue) => { response = topicValue; counter(); }); expect(response.data).toBe(null); expect(response.loading).toBe(true); expect(response.error).toBe(undefined); await resolveAfterTwo; expect(response.data).toBe(null); expect(response.loading).toBe(false); expect(response.error.graphQLErrors).toEqual(expect.arrayContaining([{ message: 'An error occurred when fetching the topic' }])); }); }); }); });