478 lines
12 KiB
TypeScript
478 lines
12 KiB
TypeScript
import chai, { expect } from 'chai'
|
|
import 'mocha'
|
|
import nock from 'nock'
|
|
import sinonChai from 'sinon-chai'
|
|
import { Integration } from '../../src/entity/integration'
|
|
import { User } from '../../src/entity/user'
|
|
import { SetIntegrationErrorCode } from '../../src/generated/graphql'
|
|
import {
|
|
deleteIntegrations,
|
|
findIntegration,
|
|
saveIntegration,
|
|
updateIntegration,
|
|
} from '../../src/services/integrations'
|
|
import { deleteUser } from '../../src/services/user'
|
|
import { createTestUser } from '../db'
|
|
import { generateFakeUuid, graphqlRequest, request } from '../util'
|
|
|
|
chai.use(sinonChai)
|
|
|
|
describe('Integrations resolvers', () => {
|
|
const READWISE_API_URL = 'https://readwise.io/api/v2'
|
|
|
|
let loginUser: User
|
|
let authToken: string
|
|
|
|
before(async () => {
|
|
// create test user and login
|
|
loginUser = await createTestUser('loginUser')
|
|
const res = await request
|
|
.post('/local/debug/fake-user-login')
|
|
.send({ fakeEmail: loginUser.email })
|
|
|
|
authToken = res.body.authToken as string
|
|
})
|
|
|
|
after(async () => {
|
|
await deleteUser(loginUser.id)
|
|
})
|
|
|
|
describe('setIntegration API', () => {
|
|
const validToken = 'valid-token'
|
|
const query = `
|
|
mutation SetIntegration($input: SetIntegrationInput!) {
|
|
setIntegration(input: $input) {
|
|
... on SetIntegrationSuccess {
|
|
integration {
|
|
id
|
|
enabled
|
|
}
|
|
}
|
|
... on SetIntegrationError {
|
|
errorCodes
|
|
}
|
|
}
|
|
}
|
|
`
|
|
let integrationId: string
|
|
let token: string
|
|
let integrationName: string
|
|
let enabled: boolean
|
|
let scope: nock.Scope
|
|
|
|
// mock Readwise Auth API
|
|
before(() => {
|
|
scope = nock(READWISE_API_URL, {
|
|
reqheaders: { Authorization: `Token ${validToken}` },
|
|
})
|
|
.get('/auth')
|
|
.reply(204)
|
|
.persist()
|
|
integrationName = 'READWISE'
|
|
enabled = true
|
|
token = 'test token'
|
|
})
|
|
|
|
after(() => {
|
|
scope.persist(false)
|
|
})
|
|
|
|
context('when id is not in the request', () => {
|
|
before(() => {
|
|
integrationId = ''
|
|
})
|
|
|
|
context('when token is invalid', () => {
|
|
before(() => {
|
|
token = 'invalid token'
|
|
nock(READWISE_API_URL, {
|
|
reqheaders: { Authorization: `Token ${token}` },
|
|
})
|
|
.get('/auth')
|
|
.reply(401)
|
|
})
|
|
|
|
it('returns InvalidToken error code', async () => {
|
|
const res = await graphqlRequest(query, authToken, {
|
|
input: {
|
|
id: integrationId,
|
|
name: integrationName,
|
|
token,
|
|
enabled,
|
|
},
|
|
})
|
|
expect(res.body.data.setIntegration.errorCodes).to.eql([
|
|
SetIntegrationErrorCode.InvalidToken,
|
|
])
|
|
})
|
|
})
|
|
|
|
context('when token is valid', () => {
|
|
before(() => {
|
|
token = validToken
|
|
})
|
|
|
|
afterEach(async () => {
|
|
await deleteIntegrations(loginUser.id, {
|
|
user: { id: loginUser.id },
|
|
name: integrationName,
|
|
})
|
|
})
|
|
|
|
it('creates new integration', async () => {
|
|
const res = await graphqlRequest(query, authToken, {
|
|
input: {
|
|
id: integrationId,
|
|
name: integrationName,
|
|
token,
|
|
enabled,
|
|
},
|
|
})
|
|
expect(res.body.data.setIntegration.integration.enabled).to.be.true
|
|
})
|
|
})
|
|
})
|
|
|
|
context('when id is in the request', () => {
|
|
let existingIntegration: Integration
|
|
|
|
context('when integration does not exist', () => {
|
|
before(() => {
|
|
integrationId = generateFakeUuid()
|
|
})
|
|
|
|
it('returns NotFound error code', async () => {
|
|
const res = await graphqlRequest(query, authToken, {
|
|
input: { id: integrationId, name: integrationName, enabled, token },
|
|
})
|
|
expect(res.body.data.setIntegration.errorCodes).to.eql([
|
|
SetIntegrationErrorCode.NotFound,
|
|
])
|
|
})
|
|
})
|
|
|
|
context('when integration exists', () => {
|
|
context('when integration does not belong to the user', () => {
|
|
let otherUser: User
|
|
|
|
before(async () => {
|
|
otherUser = await createTestUser('otherUser')
|
|
existingIntegration = await saveIntegration(
|
|
{
|
|
user: { id: otherUser.id },
|
|
name: 'READWISE',
|
|
token: 'fakeToken',
|
|
enabled,
|
|
},
|
|
otherUser.id
|
|
)
|
|
integrationId = existingIntegration.id
|
|
})
|
|
|
|
after(async () => {
|
|
await deleteUser(otherUser.id)
|
|
await deleteIntegrations(otherUser.id, [existingIntegration.id])
|
|
})
|
|
|
|
it('returns Unauthorized error code', async () => {
|
|
const res = await graphqlRequest(query, authToken, {
|
|
input: {
|
|
id: integrationId,
|
|
name: integrationName,
|
|
enabled,
|
|
token,
|
|
},
|
|
})
|
|
expect(res.body.data.setIntegration.errorCodes).to.eql([
|
|
SetIntegrationErrorCode.NotFound,
|
|
])
|
|
})
|
|
})
|
|
|
|
context('when integration belongs to the user', () => {
|
|
before(async () => {
|
|
existingIntegration = await saveIntegration(
|
|
{
|
|
user: { id: loginUser.id },
|
|
name: 'READWISE',
|
|
token: 'fakeToken',
|
|
enabled,
|
|
},
|
|
loginUser.id
|
|
)
|
|
integrationId = existingIntegration.id
|
|
})
|
|
|
|
after(async () => {
|
|
await deleteIntegrations(loginUser.id, [existingIntegration.id])
|
|
})
|
|
|
|
context('when enable is false', () => {
|
|
before(() => {
|
|
enabled = false
|
|
})
|
|
|
|
afterEach(async () => {
|
|
await updateIntegration(
|
|
existingIntegration.id,
|
|
{
|
|
taskName: 'some task name',
|
|
enabled: true,
|
|
},
|
|
loginUser.id
|
|
)
|
|
})
|
|
|
|
it('disables integration', async () => {
|
|
const res = await graphqlRequest(query, authToken, {
|
|
input: {
|
|
id: integrationId,
|
|
name: integrationName,
|
|
token,
|
|
enabled,
|
|
},
|
|
})
|
|
expect(res.body.data.setIntegration.integration.enabled).to.be
|
|
.false
|
|
})
|
|
})
|
|
|
|
context('when enable is true', () => {
|
|
before(() => {
|
|
enabled = true
|
|
})
|
|
|
|
afterEach(async () => {
|
|
await updateIntegration(
|
|
existingIntegration.id,
|
|
{
|
|
taskName: null,
|
|
enabled: false,
|
|
},
|
|
loginUser.id
|
|
)
|
|
})
|
|
|
|
it('enables integration', async () => {
|
|
const res = await graphqlRequest(query, authToken, {
|
|
input: {
|
|
id: integrationId,
|
|
name: integrationName,
|
|
token,
|
|
enabled,
|
|
},
|
|
})
|
|
expect(res.body.data.setIntegration.integration.enabled).to.be
|
|
.true
|
|
})
|
|
})
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('integrations API', () => {
|
|
const query = `
|
|
query {
|
|
integrations {
|
|
... on IntegrationsSuccess {
|
|
integrations {
|
|
id
|
|
type
|
|
enabled
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`
|
|
|
|
let existingIntegration: Integration
|
|
|
|
before(async () => {
|
|
existingIntegration = await saveIntegration(
|
|
{
|
|
user: { id: loginUser.id },
|
|
name: 'READWISE',
|
|
token: 'fakeToken',
|
|
},
|
|
loginUser.id
|
|
)
|
|
})
|
|
|
|
after(async () => {
|
|
await deleteIntegrations(loginUser.id, [existingIntegration.id])
|
|
})
|
|
|
|
it('returns all integrations', async () => {
|
|
const res = await graphqlRequest(query, authToken)
|
|
expect(res.body.data.integrations.integrations).to.have.length(1)
|
|
expect(res.body.data.integrations.integrations[0].id).to.equal(
|
|
existingIntegration.id
|
|
)
|
|
expect(res.body.data.integrations.integrations[0].type).to.equal(
|
|
existingIntegration.type
|
|
)
|
|
expect(res.body.data.integrations.integrations[0].enabled).to.equal(
|
|
existingIntegration.enabled
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('deleteIntegration API', () => {
|
|
const query = (id: string) => `
|
|
mutation {
|
|
deleteIntegration(id: "${id}") {
|
|
... on DeleteIntegrationSuccess {
|
|
integration {
|
|
id
|
|
}
|
|
}
|
|
... on DeleteIntegrationError {
|
|
errorCodes
|
|
}
|
|
}
|
|
}
|
|
`
|
|
|
|
context('when integration exists', () => {
|
|
let existingIntegration: Integration
|
|
|
|
beforeEach(async () => {
|
|
existingIntegration = await saveIntegration(
|
|
{
|
|
user: { id: loginUser.id },
|
|
name: 'READWISE',
|
|
token: 'fakeToken',
|
|
taskName: 'some task name',
|
|
},
|
|
loginUser.id
|
|
)
|
|
})
|
|
|
|
it('deletes the integration and cloud task', async () => {
|
|
const res = await graphqlRequest(
|
|
query(existingIntegration.id),
|
|
authToken
|
|
)
|
|
const integration = await findIntegration(
|
|
{
|
|
id: existingIntegration.id,
|
|
},
|
|
loginUser.id
|
|
)
|
|
|
|
expect(res.body.data.deleteIntegration.integration).to.be.an('object')
|
|
expect(res.body.data.deleteIntegration.integration.id).to.eql(
|
|
existingIntegration.id
|
|
)
|
|
expect(integration).to.be.null
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('importFromIntegration API', () => {
|
|
const query = (integrationId: string) => `
|
|
mutation {
|
|
importFromIntegration(integrationId: "${integrationId}") {
|
|
... on ImportFromIntegrationSuccess {
|
|
success
|
|
}
|
|
... on ImportFromIntegrationError {
|
|
errorCodes
|
|
}
|
|
}
|
|
}
|
|
`
|
|
let existingIntegration: Integration
|
|
|
|
context('when integration exists', () => {
|
|
before(async () => {
|
|
existingIntegration = await saveIntegration(
|
|
{
|
|
user: { id: loginUser.id },
|
|
name: 'POCKET',
|
|
token: 'fakeToken',
|
|
},
|
|
loginUser.id
|
|
)
|
|
})
|
|
|
|
after(async () => {
|
|
await deleteIntegrations(loginUser.id, [existingIntegration.id])
|
|
})
|
|
|
|
it('returns success and starts cloud task', async () => {
|
|
const res = await graphqlRequest(
|
|
query(existingIntegration.id),
|
|
authToken
|
|
).expect(200)
|
|
expect(res.body.data.importFromIntegration.success).to.be.true
|
|
})
|
|
})
|
|
|
|
context('when integration does not exist', () => {
|
|
it('returns error', async () => {
|
|
const invalidIntegrationId = generateFakeUuid()
|
|
const res = await graphqlRequest(
|
|
query(invalidIntegrationId),
|
|
authToken
|
|
).expect(200)
|
|
expect(res.body.data.importFromIntegration.errorCodes).to.eql([
|
|
'UNAUTHORIZED',
|
|
])
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('integration API', () => {
|
|
const query = `
|
|
query Integration ($name: String!) {
|
|
integration(name: $name) {
|
|
... on IntegrationSuccess {
|
|
integration {
|
|
id
|
|
type
|
|
enabled
|
|
}
|
|
}
|
|
... on IntegrationError {
|
|
errorCodes
|
|
}
|
|
}
|
|
}
|
|
`
|
|
|
|
let existingIntegration: Integration
|
|
|
|
before(async () => {
|
|
existingIntegration = await saveIntegration(
|
|
{
|
|
user: { id: loginUser.id },
|
|
name: 'READWISE',
|
|
token: 'fakeToken',
|
|
},
|
|
loginUser.id
|
|
)
|
|
})
|
|
|
|
after(async () => {
|
|
await deleteIntegrations(loginUser.id, [existingIntegration.id])
|
|
})
|
|
|
|
it('returns the integration', async () => {
|
|
const res = await graphqlRequest(query, authToken, {
|
|
name: existingIntegration.name,
|
|
})
|
|
expect(res.body.data.integration.integration.id).to.equal(
|
|
existingIntegration.id
|
|
)
|
|
expect(res.body.data.integration.integration.type).to.equal(
|
|
existingIntegration.type
|
|
)
|
|
expect(res.body.data.integration.integration.enabled).to.equal(
|
|
existingIntegration.enabled
|
|
)
|
|
})
|
|
})
|
|
})
|