diff --git a/tests/mirage/categories-test.js b/tests/mirage/categories-test.js deleted file mode 100644 index cb1aabad038..00000000000 --- a/tests/mirage/categories-test.js +++ /dev/null @@ -1,207 +0,0 @@ -import { module, test } from 'qunit'; - -import fetch from 'fetch'; - -import { setupTest } from 'cargo/tests/helpers'; - -import setupMirage from '../helpers/setup-mirage'; - -module('Mirage | Categories', function (hooks) { - setupTest(hooks); - setupMirage(hooks); - - module('GET /api/v1/categories', function () { - test('empty case', async function (assert) { - let response = await fetch('/api/v1/categories'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - categories: [], - meta: { - total: 0, - }, - }); - }); - - test('returns a paginated categories list', async function (assert) { - this.server.create('category', { - category: 'no-std', - description: 'Crates that are able to function without the Rust standard library.', - }); - this.server.createList('category', 2); - - let response = await fetch('/api/v1/categories'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - categories: [ - { - id: 'category-1', - category: 'Category 1', - crates_cnt: 0, - created_at: '2010-06-16T21:30:45Z', - description: 'This is the description for the category called "Category 1"', - slug: 'category-1', - }, - { - id: 'category-2', - category: 'Category 2', - crates_cnt: 0, - created_at: '2010-06-16T21:30:45Z', - description: 'This is the description for the category called "Category 2"', - slug: 'category-2', - }, - { - id: 'no-std', - category: 'no-std', - crates_cnt: 0, - created_at: '2010-06-16T21:30:45Z', - description: 'Crates that are able to function without the Rust standard library.', - slug: 'no-std', - }, - ], - meta: { - total: 3, - }, - }); - }); - - test('never returns more than 10 results', async function (assert) { - this.server.createList('category', 25); - - let response = await fetch('/api/v1/categories'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.equal(responsePayload.categories.length, 10); - assert.equal(responsePayload.meta.total, 25); - }); - - test('supports `page` and `per_page` parameters', async function (assert) { - this.server.createList('category', 25, { - category: i => `cat-${String(i + 1).padStart(2, '0')}`, - }); - - let response = await fetch('/api/v1/categories?page=2&per_page=5'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.equal(responsePayload.categories.length, 5); - assert.deepEqual( - responsePayload.categories.map(it => it.id), - ['cat-06', 'cat-07', 'cat-08', 'cat-09', 'cat-10'], - ); - assert.equal(responsePayload.meta.total, 25); - }); - }); - - module('GET /api/v1/categories/:id', function () { - test('returns 404 for unknown categories', async function (assert) { - let response = await fetch('/api/v1/categories/foo'); - assert.equal(response.status, 404); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { errors: [{ detail: 'Not Found' }] }); - }); - - test('returns a category object for known categories', async function (assert) { - this.server.create('category', { - category: 'no-std', - description: 'Crates that are able to function without the Rust standard library.', - }); - - let response = await fetch('/api/v1/categories/no-std'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - category: { - id: 'no-std', - category: 'no-std', - crates_cnt: 0, - created_at: '2010-06-16T21:30:45Z', - description: 'Crates that are able to function without the Rust standard library.', - slug: 'no-std', - }, - }); - }); - - test('calculates `crates_cnt` correctly', async function (assert) { - this.server.create('category', { category: 'cli' }); - this.server.createList('crate', 7, { categoryIds: ['cli'] }); - this.server.create('category', { category: 'not-cli' }); - this.server.createList('crate', 3, { categoryIds: ['not-cli'] }); - - let response = await fetch('/api/v1/categories/cli'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - category: { - category: 'cli', - crates_cnt: 7, - created_at: '2010-06-16T21:30:45Z', - description: 'This is the description for the category called "cli"', - id: 'cli', - slug: 'cli', - }, - }); - }); - }); - - module('GET /api/v1/category_slugs', function () { - test('empty case', async function (assert) { - let response = await fetch('/api/v1/category_slugs'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - category_slugs: [], - }); - }); - - test('returns a category slugs list', async function (assert) { - this.server.create('category', { - category: 'no-std', - description: 'Crates that are able to function without the Rust standard library.', - }); - this.server.createList('category', 2); - - let response = await fetch('/api/v1/category_slugs'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - category_slugs: [ - { - description: 'This is the description for the category called "Category 1"', - id: 'category-1', - slug: 'category-1', - }, - { - description: 'This is the description for the category called "Category 2"', - id: 'category-2', - slug: 'category-2', - }, - { - description: 'Crates that are able to function without the Rust standard library.', - id: 'no-std', - slug: 'no-std', - }, - ], - }); - }); - - test('has no pagination', async function (assert) { - this.server.createList('category', 25); - - let response = await fetch('/api/v1/category_slugs'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.equal(responsePayload.category_slugs.length, 25); - }); - }); -}); diff --git a/tests/mirage/categories/get-by-id-test.js b/tests/mirage/categories/get-by-id-test.js new file mode 100644 index 00000000000..959fb1eaf75 --- /dev/null +++ b/tests/mirage/categories/get-by-id-test.js @@ -0,0 +1,57 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; + +import { setupTest } from '../../helpers'; +import setupMirage from '../../helpers/setup-mirage'; + +module('Mirage | GET /api/v1/categories/:id', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('returns 404 for unknown categories', async function (assert) { + let response = await fetch('/api/v1/categories/foo'); + assert.equal(response.status, 404); + assert.deepEqual(await response.json(), { errors: [{ detail: 'Not Found' }] }); + }); + + test('returns a category object for known categories', async function (assert) { + this.server.create('category', { + category: 'no-std', + description: 'Crates that are able to function without the Rust standard library.', + }); + + let response = await fetch('/api/v1/categories/no-std'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + category: { + id: 'no-std', + category: 'no-std', + crates_cnt: 0, + created_at: '2010-06-16T21:30:45Z', + description: 'Crates that are able to function without the Rust standard library.', + slug: 'no-std', + }, + }); + }); + + test('calculates `crates_cnt` correctly', async function (assert) { + this.server.create('category', { category: 'cli' }); + this.server.createList('crate', 7, { categoryIds: ['cli'] }); + this.server.create('category', { category: 'not-cli' }); + this.server.createList('crate', 3, { categoryIds: ['not-cli'] }); + + let response = await fetch('/api/v1/categories/cli'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + category: { + category: 'cli', + crates_cnt: 7, + created_at: '2010-06-16T21:30:45Z', + description: 'This is the description for the category called "cli"', + id: 'cli', + slug: 'cli', + }, + }); + }); +}); diff --git a/tests/mirage/categories/list-test.js b/tests/mirage/categories/list-test.js new file mode 100644 index 00000000000..b87d3907267 --- /dev/null +++ b/tests/mirage/categories/list-test.js @@ -0,0 +1,92 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; + +import { setupTest } from '../../helpers'; +import setupMirage from '../../helpers/setup-mirage'; + +module('Mirage | GET /api/v1/categories', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('empty case', async function (assert) { + let response = await fetch('/api/v1/categories'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + categories: [], + meta: { + total: 0, + }, + }); + }); + + test('returns a paginated categories list', async function (assert) { + this.server.create('category', { + category: 'no-std', + description: 'Crates that are able to function without the Rust standard library.', + }); + this.server.createList('category', 2); + + let response = await fetch('/api/v1/categories'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + categories: [ + { + id: 'category-1', + category: 'Category 1', + crates_cnt: 0, + created_at: '2010-06-16T21:30:45Z', + description: 'This is the description for the category called "Category 1"', + slug: 'category-1', + }, + { + id: 'category-2', + category: 'Category 2', + crates_cnt: 0, + created_at: '2010-06-16T21:30:45Z', + description: 'This is the description for the category called "Category 2"', + slug: 'category-2', + }, + { + id: 'no-std', + category: 'no-std', + crates_cnt: 0, + created_at: '2010-06-16T21:30:45Z', + description: 'Crates that are able to function without the Rust standard library.', + slug: 'no-std', + }, + ], + meta: { + total: 3, + }, + }); + }); + + test('never returns more than 10 results', async function (assert) { + this.server.createList('category', 25); + + let response = await fetch('/api/v1/categories'); + assert.equal(response.status, 200); + + let responsePayload = await response.json(); + assert.equal(responsePayload.categories.length, 10); + assert.equal(responsePayload.meta.total, 25); + }); + + test('supports `page` and `per_page` parameters', async function (assert) { + this.server.createList('category', 25, { + category: i => `cat-${String(i + 1).padStart(2, '0')}`, + }); + + let response = await fetch('/api/v1/categories?page=2&per_page=5'); + assert.equal(response.status, 200); + + let responsePayload = await response.json(); + assert.equal(responsePayload.categories.length, 5); + assert.deepEqual( + responsePayload.categories.map(it => it.id), + ['cat-06', 'cat-07', 'cat-08', 'cat-09', 'cat-10'], + ); + assert.equal(responsePayload.meta.total, 25); + }); +}); diff --git a/tests/mirage/category-slugs/list-test.js b/tests/mirage/category-slugs/list-test.js new file mode 100644 index 00000000000..cd76c34e2da --- /dev/null +++ b/tests/mirage/category-slugs/list-test.js @@ -0,0 +1,57 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; + +import { setupTest } from '../../helpers'; +import setupMirage from '../../helpers/setup-mirage'; + +module('Mirage | GET /api/v1/category_slugs', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('empty case', async function (assert) { + let response = await fetch('/api/v1/category_slugs'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + category_slugs: [], + }); + }); + + test('returns a category slugs list', async function (assert) { + this.server.create('category', { + category: 'no-std', + description: 'Crates that are able to function without the Rust standard library.', + }); + this.server.createList('category', 2); + + let response = await fetch('/api/v1/category_slugs'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + category_slugs: [ + { + description: 'This is the description for the category called "Category 1"', + id: 'category-1', + slug: 'category-1', + }, + { + description: 'This is the description for the category called "Category 2"', + id: 'category-2', + slug: 'category-2', + }, + { + description: 'Crates that are able to function without the Rust standard library.', + id: 'no-std', + slug: 'no-std', + }, + ], + }); + }); + + test('has no pagination', async function (assert) { + this.server.createList('category', 25); + + let response = await fetch('/api/v1/category_slugs'); + assert.equal(response.status, 200); + assert.equal((await response.json()).category_slugs.length, 25); + }); +}); diff --git a/tests/mirage/confirm/put-by-id-test.js b/tests/mirage/confirm/put-by-id-test.js new file mode 100644 index 00000000000..8c3727eb016 --- /dev/null +++ b/tests/mirage/confirm/put-by-id-test.js @@ -0,0 +1,45 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; + +import { setupTest } from '../../helpers'; +import setupMirage from '../../helpers/setup-mirage'; + +module('Mirage | PUT /api/v1/confirm/:token', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('returns `ok: true` for a known token (unauthenticated)', async function (assert) { + let user = this.server.create('user', { emailVerificationToken: 'foo' }); + assert.strictEqual(user.emailVerified, false); + + let response = await fetch('/api/v1/confirm/foo', { method: 'PUT' }); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { ok: true }); + + user.reload(); + assert.strictEqual(user.emailVerified, true); + }); + + test('returns `ok: true` for a known token (authenticated)', async function (assert) { + let user = this.server.create('user', { emailVerificationToken: 'foo' }); + assert.strictEqual(user.emailVerified, false); + + this.server.create('mirage-session', { user }); + + let response = await fetch('/api/v1/confirm/foo', { method: 'PUT' }); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { ok: true }); + + user.reload(); + assert.strictEqual(user.emailVerified, true); + }); + + test('returns an error for unknown tokens', async function (assert) { + let response = await fetch('/api/v1/confirm/unknown', { method: 'PUT' }); + assert.equal(response.status, 400); + assert.deepEqual(await response.json(), { + errors: [{ detail: 'Email belonging to token not found.' }], + }); + }); +}); diff --git a/tests/mirage/crates-test.js b/tests/mirage/crates-test.js deleted file mode 100644 index d3d9dc3baca..00000000000 --- a/tests/mirage/crates-test.js +++ /dev/null @@ -1,1158 +0,0 @@ -import { module, test } from 'qunit'; - -import fetch from 'fetch'; - -import { setupTest } from 'cargo/tests/helpers'; - -import setupMirage from '../helpers/setup-mirage'; - -module('Mirage | Crates', function (hooks) { - setupTest(hooks); - setupMirage(hooks); - - module('GET /api/v1/crates', function () { - test('empty case', async function (assert) { - let response = await fetch('/api/v1/crates'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - crates: [], - meta: { - total: 0, - }, - }); - }); - - test('returns a paginated crates list', async function (assert) { - let crate = this.server.create('crate', { name: 'rand' }); - this.server.create('version', { - crate, - created_at: '2020-11-06T12:34:56Z', - num: '1.0.0', - updated_at: '2020-11-06T12:34:56Z', - }); - this.server.create('version', { - crate, - created_at: '2020-12-25T12:34:56Z', - num: '2.0.0-beta.1', - updated_at: '2020-12-25T12:34:56Z', - }); - - let response = await fetch('/api/v1/crates'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - crates: [ - { - id: 'rand', - badges: [], - categories: [], - created_at: '2010-06-16T21:30:45Z', - description: 'This is the description for the crate called "rand"', - documentation: null, - downloads: 0, - homepage: null, - keywords: [], - links: { - owner_team: '/api/v1/crates/rand/owner_team', - owner_user: '/api/v1/crates/rand/owner_user', - reverse_dependencies: '/api/v1/crates/rand/reverse_dependencies', - version_downloads: '/api/v1/crates/rand/downloads', - versions: '/api/v1/crates/rand/versions', - }, - max_version: '2.0.0-beta.1', - max_stable_version: '1.0.0', - name: 'rand', - newest_version: '2.0.0-beta.1', - repository: null, - updated_at: '2017-02-24T12:34:56Z', - versions: ['1', '2'], - }, - ], - meta: { - total: 1, - }, - }); - }); - - test('never returns more than 10 results', async function (assert) { - let crates = this.server.createList('crate', 25); - this.server.createList('version', crates.length, { crate: i => crates[i] }); - - let response = await fetch('/api/v1/crates'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.equal(responsePayload.crates.length, 10); - assert.equal(responsePayload.meta.total, 25); - }); - - test('supports `page` and `per_page` parameters', async function (assert) { - let crates = this.server.createList('crate', 25, { - name: i => `crate-${String(i + 1).padStart(2, '0')}`, - }); - this.server.createList('version', crates.length, { crate: i => crates[i] }); - - let response = await fetch('/api/v1/crates?page=2&per_page=5'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.equal(responsePayload.crates.length, 5); - assert.deepEqual( - responsePayload.crates.map(it => it.id), - ['crate-06', 'crate-07', 'crate-08', 'crate-09', 'crate-10'], - ); - assert.equal(responsePayload.meta.total, 25); - }); - - test('supports a `letter` parameter', async function (assert) { - let foo = this.server.create('crate', { name: 'foo' }); - this.server.create('version', { crate: foo }); - let bar = this.server.create('crate', { name: 'bar' }); - this.server.create('version', { crate: bar }); - let baz = this.server.create('crate', { name: 'BAZ' }); - this.server.create('version', { crate: baz }); - - let response = await fetch('/api/v1/crates?letter=b'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.equal(responsePayload.crates.length, 2); - assert.deepEqual( - responsePayload.crates.map(it => it.id), - ['bar', 'BAZ'], - ); - assert.equal(responsePayload.meta.total, 2); - }); - - test('supports a `q` parameter', async function (assert) { - let crate1 = this.server.create('crate', { name: '123456' }); - this.server.create('version', { crate: crate1 }); - let crate2 = this.server.create('crate', { name: '00123' }); - this.server.create('version', { crate: crate2 }); - let crate3 = this.server.create('crate', { name: '87654' }); - this.server.create('version', { crate: crate3 }); - - let response = await fetch('/api/v1/crates?q=123'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.equal(responsePayload.crates.length, 2); - assert.deepEqual( - responsePayload.crates.map(it => it.id), - ['123456', '00123'], - ); - assert.equal(responsePayload.meta.total, 2); - }); - - test('supports a `user_id` parameter', async function (assert) { - let user1 = this.server.create('user'); - let user2 = this.server.create('user'); - - let foo = this.server.create('crate', { name: 'foo' }); - this.server.create('version', { crate: foo }); - let bar = this.server.create('crate', { name: 'bar' }); - this.server.create('crate-ownership', { crate: bar, user: user1 }); - this.server.create('version', { crate: bar }); - let baz = this.server.create('crate', { name: 'baz' }); - this.server.create('crate-ownership', { crate: baz, user: user2 }); - this.server.create('version', { crate: baz }); - - let response = await fetch(`/api/v1/crates?user_id=${user1.id}`); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.equal(responsePayload.crates.length, 1); - assert.equal(responsePayload.crates[0].id, 'bar'); - assert.equal(responsePayload.meta.total, 1); - }); - - test('supports a `team_id` parameter', async function (assert) { - let team1 = this.server.create('team'); - let team2 = this.server.create('team'); - - let foo = this.server.create('crate', { name: 'foo' }); - this.server.create('version', { crate: foo }); - let bar = this.server.create('crate', { name: 'bar' }); - this.server.create('crate-ownership', { crate: bar, team: team1 }); - this.server.create('version', { crate: bar }); - let baz = this.server.create('crate', { name: 'baz' }); - this.server.create('crate-ownership', { crate: baz, team: team2 }); - this.server.create('version', { crate: baz }); - - let response = await fetch(`/api/v1/crates?team_id=${team1.id}`); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.equal(responsePayload.crates.length, 1); - assert.equal(responsePayload.crates[0].id, 'bar'); - assert.equal(responsePayload.meta.total, 1); - }); - - test('supports a `following` parameter', async function (assert) { - let foo = this.server.create('crate', { name: 'foo' }); - this.server.create('version', { crate: foo }); - let bar = this.server.create('crate', { name: 'bar' }); - this.server.create('version', { crate: bar }); - - let user = this.server.create('user', { followedCrates: [bar] }); - this.authenticateAs(user); - - let response = await fetch(`/api/v1/crates?following=1`); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.equal(responsePayload.crates.length, 1); - assert.equal(responsePayload.crates[0].id, 'bar'); - assert.equal(responsePayload.meta.total, 1); - }); - - test('supports multiple `ids[]` parameters', async function (assert) { - let foo = this.server.create('crate', { name: 'foo' }); - this.server.create('version', { crate: foo }); - let bar = this.server.create('crate', { name: 'bar' }); - this.server.create('version', { crate: bar }); - let baz = this.server.create('crate', { name: 'baz' }); - this.server.create('version', { crate: baz }); - let other = this.server.create('crate', { name: 'other' }); - this.server.create('version', { crate: other }); - - let response = await fetch(`/api/v1/crates?ids[]=foo&ids[]=bar&ids[]=baz&ids[]=baz&ids[]=unknown`); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.equal(responsePayload.crates.length, 3); - assert.equal(responsePayload.crates[0].id, 'foo'); - assert.equal(responsePayload.crates[1].id, 'bar'); - assert.equal(responsePayload.crates[2].id, 'baz'); - assert.equal(responsePayload.meta.total, 3); - }); - }); - - module('GET /api/v1/crates/:id', function () { - test('returns 404 for unknown crates', async function (assert) { - let response = await fetch('/api/v1/crates/foo'); - assert.equal(response.status, 404); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { errors: [{ detail: 'Not Found' }] }); - }); - - test('returns a crate object for known crates', async function (assert) { - let crate = this.server.create('crate', { name: 'rand' }); - this.server.create('version', { crate, num: '1.0.0-beta.1' }); - - let response = await fetch('/api/v1/crates/rand'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - categories: [], - crate: { - badges: [], - categories: [], - created_at: '2010-06-16T21:30:45Z', - description: 'This is the description for the crate called "rand"', - documentation: null, - downloads: 0, - homepage: null, - id: 'rand', - keywords: [], - links: { - owner_team: '/api/v1/crates/rand/owner_team', - owner_user: '/api/v1/crates/rand/owner_user', - reverse_dependencies: '/api/v1/crates/rand/reverse_dependencies', - version_downloads: '/api/v1/crates/rand/downloads', - versions: '/api/v1/crates/rand/versions', - }, - max_version: '1.0.0-beta.1', - max_stable_version: null, - name: 'rand', - newest_version: '1.0.0-beta.1', - repository: null, - updated_at: '2017-02-24T12:34:56Z', - versions: ['1'], - }, - keywords: [], - versions: [ - { - id: '1', - crate: 'rand', - crate_size: 0, - created_at: '2010-06-16T21:30:45Z', - dl_path: '/api/v1/crates/rand/1.0.0-beta.1/download', - downloads: 0, - license: 'MIT/Apache-2.0', - links: { - dependencies: '/api/v1/crates/rand/1.0.0-beta.1/dependencies', - version_downloads: '/api/v1/crates/rand/1.0.0-beta.1/downloads', - }, - num: '1.0.0-beta.1', - published_by: null, - updated_at: '2017-02-24T12:34:56Z', - yanked: false, - }, - ], - }); - }); - - test('includes related versions', async function (assert) { - let crate = this.server.create('crate', { name: 'rand' }); - this.server.create('version', { crate, num: '1.0.0' }); - this.server.create('version', { crate, num: '1.1.0' }); - this.server.create('version', { crate, num: '1.2.0' }); - - let response = await fetch('/api/v1/crates/rand'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload.crate.versions, ['1', '2', '3']); - assert.deepEqual(responsePayload.versions, [ - { - id: '1', - crate: 'rand', - crate_size: 0, - created_at: '2010-06-16T21:30:45Z', - dl_path: '/api/v1/crates/rand/1.0.0/download', - downloads: 0, - license: 'MIT/Apache-2.0', - links: { - dependencies: '/api/v1/crates/rand/1.0.0/dependencies', - version_downloads: '/api/v1/crates/rand/1.0.0/downloads', - }, - num: '1.0.0', - published_by: null, - updated_at: '2017-02-24T12:34:56Z', - yanked: false, - }, - { - id: '2', - crate: 'rand', - crate_size: 162_963, - created_at: '2010-06-16T21:30:45Z', - dl_path: '/api/v1/crates/rand/1.1.0/download', - downloads: 3702, - license: 'MIT', - links: { - dependencies: '/api/v1/crates/rand/1.1.0/dependencies', - version_downloads: '/api/v1/crates/rand/1.1.0/downloads', - }, - num: '1.1.0', - published_by: null, - updated_at: '2017-02-24T12:34:56Z', - yanked: false, - }, - { - id: '3', - crate: 'rand', - crate_size: 325_926, - created_at: '2010-06-16T21:30:45Z', - dl_path: '/api/v1/crates/rand/1.2.0/download', - downloads: 7404, - license: 'Apache-2.0', - links: { - dependencies: '/api/v1/crates/rand/1.2.0/dependencies', - version_downloads: '/api/v1/crates/rand/1.2.0/downloads', - }, - num: '1.2.0', - published_by: null, - updated_at: '2017-02-24T12:34:56Z', - yanked: false, - }, - ]); - }); - - test('includes related categories', async function (assert) { - this.server.create('category', { category: 'no-std' }); - this.server.create('category', { category: 'cli' }); - let crate = this.server.create('crate', { name: 'rand', categoryIds: ['no-std'] }); - this.server.create('version', { crate }); - - let response = await fetch('/api/v1/crates/rand'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload.crate.categories, ['no-std']); - assert.deepEqual(responsePayload.categories, [ - { - id: 'no-std', - category: 'no-std', - crates_cnt: 1, - created_at: '2010-06-16T21:30:45Z', - description: 'This is the description for the category called "no-std"', - slug: 'no-std', - }, - ]); - }); - - test('includes related keywords', async function (assert) { - this.server.create('keyword', { keyword: 'no-std' }); - this.server.create('keyword', { keyword: 'cli' }); - let crate = this.server.create('crate', { name: 'rand', keywordIds: ['no-std'] }); - this.server.create('version', { crate }); - - let response = await fetch('/api/v1/crates/rand'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload.crate.keywords, ['no-std']); - assert.deepEqual(responsePayload.keywords, [ - { - crates_cnt: 1, - id: 'no-std', - keyword: 'no-std', - }, - ]); - }); - }); - - module('GET /api/v1/crates/:crateId/following', function () { - test('returns 403 if unauthenticated', async function (assert) { - let response = await fetch('/api/v1/crates/foo/following'); - assert.equal(response.status, 403); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - errors: [{ detail: 'must be logged in to perform that action' }], - }); - }); - - test('returns 404 for unknown crates', async function (assert) { - let user = this.server.create('user'); - this.authenticateAs(user); - - let response = await fetch('/api/v1/crates/foo/following'); - assert.equal(response.status, 404); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { errors: [{ detail: 'Not Found' }] }); - }); - - test('returns true if the authenticated user follows the crate', async function (assert) { - let crate = this.server.create('crate', { name: 'rand' }); - - let user = this.server.create('user', { followedCrates: [crate] }); - this.authenticateAs(user); - - let response = await fetch('/api/v1/crates/rand/following'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { following: true }); - }); - - test('returns false if the authenticated user is not following the crate', async function (assert) { - this.server.create('crate', { name: 'rand' }); - - let user = this.server.create('user'); - this.authenticateAs(user); - - let response = await fetch('/api/v1/crates/rand/following'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { following: false }); - }); - }); - - module('PUT /api/v1/crates/:crateId/follow', function () { - test('returns 403 if unauthenticated', async function (assert) { - let response = await fetch('/api/v1/crates/foo/follow', { method: 'PUT' }); - assert.equal(response.status, 403); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - errors: [{ detail: 'must be logged in to perform that action' }], - }); - }); - - test('returns 404 for unknown crates', async function (assert) { - let user = this.server.create('user'); - this.authenticateAs(user); - - let response = await fetch('/api/v1/crates/foo/follow', { method: 'PUT' }); - assert.equal(response.status, 404); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { errors: [{ detail: 'Not Found' }] }); - }); - - test('makes the authenticated user follow the crate', async function (assert) { - let crate = this.server.create('crate', { name: 'rand' }); - - let user = this.server.create('user'); - this.authenticateAs(user); - - assert.deepEqual(user.followedCrateIds, []); - - let response = await fetch('/api/v1/crates/rand/follow', { method: 'PUT' }); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { ok: true }); - - user.reload(); - assert.deepEqual(user.followedCrateIds, [crate.id]); - }); - }); - - module('DELETE /api/v1/crates/:crateId/follow', function () { - test('returns 403 if unauthenticated', async function (assert) { - let response = await fetch('/api/v1/crates/foo/follow', { method: 'DELETE' }); - assert.equal(response.status, 403); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - errors: [{ detail: 'must be logged in to perform that action' }], - }); - }); - - test('returns 404 for unknown crates', async function (assert) { - let user = this.server.create('user'); - this.authenticateAs(user); - - let response = await fetch('/api/v1/crates/foo/follow', { method: 'DELETE' }); - assert.equal(response.status, 404); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { errors: [{ detail: 'Not Found' }] }); - }); - - test('makes the authenticated user unfollow the crate', async function (assert) { - let crate = this.server.create('crate', { name: 'rand' }); - - let user = this.server.create('user', { followedCrates: [crate] }); - this.authenticateAs(user); - - assert.deepEqual(user.followedCrateIds, [crate.id]); - - let response = await fetch('/api/v1/crates/rand/follow', { method: 'DELETE' }); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { ok: true }); - - user.reload(); - assert.deepEqual(user.followedCrateIds, []); - }); - }); - - module('GET /api/v1/crates/:id/versions', function () { - test('returns 404 for unknown crates', async function (assert) { - let response = await fetch('/api/v1/crates/foo/versions'); - assert.equal(response.status, 404); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { errors: [{ detail: 'Not Found' }] }); - }); - - test('empty case', async function (assert) { - this.server.create('crate', { name: 'rand' }); - - let response = await fetch('/api/v1/crates/rand/versions'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - versions: [], - }); - }); - - test('returns all versions belonging to the specified crate', async function (assert) { - let user = this.server.create('user'); - let crate = this.server.create('crate', { name: 'rand' }); - this.server.create('version', { crate, num: '1.0.0' }); - this.server.create('version', { crate, num: '1.1.0', publishedBy: user }); - this.server.create('version', { crate, num: '1.2.0' }); - - let response = await fetch('/api/v1/crates/rand/versions'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - versions: [ - { - id: '1', - crate: 'rand', - crate_size: 0, - created_at: '2010-06-16T21:30:45Z', - dl_path: '/api/v1/crates/rand/1.0.0/download', - downloads: 0, - license: 'MIT/Apache-2.0', - links: { - dependencies: '/api/v1/crates/rand/1.0.0/dependencies', - version_downloads: '/api/v1/crates/rand/1.0.0/downloads', - }, - num: '1.0.0', - published_by: null, - updated_at: '2017-02-24T12:34:56Z', - yanked: false, - }, - { - id: '2', - crate: 'rand', - crate_size: 162_963, - created_at: '2010-06-16T21:30:45Z', - dl_path: '/api/v1/crates/rand/1.1.0/download', - downloads: 3702, - license: 'MIT', - links: { - dependencies: '/api/v1/crates/rand/1.1.0/dependencies', - version_downloads: '/api/v1/crates/rand/1.1.0/downloads', - }, - num: '1.1.0', - published_by: { - id: 1, - avatar: 'https://avatars1.githubusercontent.com/u/14631425?v=4', - login: 'user-1', - name: 'User 1', - url: 'https://github.com/user-1', - }, - updated_at: '2017-02-24T12:34:56Z', - yanked: false, - }, - { - id: '3', - crate: 'rand', - crate_size: 325_926, - created_at: '2010-06-16T21:30:45Z', - dl_path: '/api/v1/crates/rand/1.2.0/download', - downloads: 7404, - license: 'Apache-2.0', - links: { - dependencies: '/api/v1/crates/rand/1.2.0/dependencies', - version_downloads: '/api/v1/crates/rand/1.2.0/downloads', - }, - num: '1.2.0', - published_by: null, - updated_at: '2017-02-24T12:34:56Z', - yanked: false, - }, - ], - }); - }); - }); - - module('GET /api/v1/crates/:id/:version/authors', function () { - test('returns 404 for unknown crates', async function (assert) { - let response = await fetch('/api/v1/crates/foo/1.0.0/authors'); - assert.equal(response.status, 404); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { errors: [{ detail: 'Not Found' }] }); - }); - - test('returns 200 for unknown versions', async function (assert) { - this.server.create('crate', { name: 'rand' }); - - let response = await fetch('/api/v1/crates/rand/1.0.0/authors'); - // we should probably return 404 for this, but the production API - // currently doesn't do this either - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { errors: [{ detail: 'crate `rand` does not have a version `1.0.0`' }] }); - }); - - test('empty case', async function (assert) { - let crate = this.server.create('crate', { name: 'rand' }); - this.server.create('version', { crate, num: '1.0.0' }); - - let response = await fetch('/api/v1/crates/rand/1.0.0/authors'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - meta: { - names: [], - }, - users: [], - }); - }); - - test('returns a list of authors belonging to the specified crate version', async function (assert) { - let crate = this.server.create('crate', { name: 'rand' }); - this.server.create('version', { crate, num: '1.0.0' }); - - let response = await fetch('/api/v1/crates/rand/1.0.0/authors'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - meta: { - names: [], - }, - users: [], - }); - }); - }); - - module('GET /api/v1/crates/:id/:version/dependencies', function () { - test('returns 404 for unknown crates', async function (assert) { - let response = await fetch('/api/v1/crates/foo/1.0.0/dependencies'); - assert.equal(response.status, 404); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { errors: [{ detail: 'Not Found' }] }); - }); - - test('returns 200 for unknown versions', async function (assert) { - this.server.create('crate', { name: 'rand' }); - - let response = await fetch('/api/v1/crates/rand/1.0.0/dependencies'); - // we should probably return 404 for this, but the production API - // currently doesn't do this either - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { errors: [{ detail: 'crate `rand` does not have a version `1.0.0`' }] }); - }); - - test('empty case', async function (assert) { - let crate = this.server.create('crate', { name: 'rand' }); - this.server.create('version', { crate, num: '1.0.0' }); - - let response = await fetch('/api/v1/crates/rand/1.0.0/dependencies'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - dependencies: [], - }); - }); - - test('returns a list of dependencies belonging to the specified crate version', async function (assert) { - let crate = this.server.create('crate', { name: 'rand' }); - let version = this.server.create('version', { crate, num: '1.0.0' }); - - let foo = this.server.create('crate', { name: 'foo' }); - this.server.create('dependency', { crate: foo, version }); - let bar = this.server.create('crate', { name: 'bar' }); - this.server.create('dependency', { crate: bar, version }); - let baz = this.server.create('crate', { name: 'baz' }); - this.server.create('dependency', { crate: baz, version }); - - let response = await fetch('/api/v1/crates/rand/1.0.0/dependencies'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - dependencies: [ - { - id: '1', - crate_id: 'foo', - default_features: false, - features: [], - kind: 'dev', - optional: true, - req: '^0.1.0', - target: null, - version_id: '1', - }, - { - id: '2', - crate_id: 'bar', - default_features: false, - features: [], - kind: 'normal', - optional: true, - req: '^2.1.3', - target: null, - version_id: '1', - }, - { - id: '3', - crate_id: 'baz', - default_features: false, - features: [], - kind: 'normal', - optional: true, - req: '0.3.7', - target: null, - version_id: '1', - }, - ], - }); - }); - }); - - module('GET /api/v1/crates/:id/:version/downloads', function () { - test('returns 404 for unknown crates', async function (assert) { - let response = await fetch('/api/v1/crates/foo/1.0.0/downloads'); - assert.equal(response.status, 404); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { errors: [{ detail: 'Not Found' }] }); - }); - - test('returns 200 for unknown versions', async function (assert) { - this.server.create('crate', { name: 'rand' }); - - let response = await fetch('/api/v1/crates/rand/1.0.0/downloads'); - // we should probably return 404 for this, but the production API - // currently doesn't do this either - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { errors: [{ detail: 'crate `rand` does not have a version `1.0.0`' }] }); - }); - - test('empty case', async function (assert) { - let crate = this.server.create('crate', { name: 'rand' }); - this.server.create('version', { crate, num: '1.0.0' }); - - let response = await fetch('/api/v1/crates/rand/1.0.0/downloads'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - version_downloads: [], - }); - }); - - test('returns a list of version downloads belonging to the specified crate version', async function (assert) { - let crate = this.server.create('crate', { name: 'rand' }); - let version = this.server.create('version', { crate, num: '1.0.0' }); - this.server.create('version-download', { version, date: '2020-01-13' }); - this.server.create('version-download', { version, date: '2020-01-14' }); - this.server.create('version-download', { version, date: '2020-01-15' }); - - let response = await fetch('/api/v1/crates/rand/1.0.0/downloads'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - version_downloads: [ - { - date: '2020-01-13', - downloads: 9380, - version: '1', - }, - { - date: '2020-01-14', - downloads: 16_415, - version: '1', - }, - { - date: '2020-01-15', - downloads: 23_450, - version: '1', - }, - ], - }); - }); - }); - - module('GET /api/v1/crates/:id/owner_user', function () { - test('returns 404 for unknown crates', async function (assert) { - let response = await fetch('/api/v1/crates/foo/owner_user'); - assert.equal(response.status, 404); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { errors: [{ detail: 'Not Found' }] }); - }); - - test('empty case', async function (assert) { - this.server.create('crate', { name: 'rand' }); - - let response = await fetch('/api/v1/crates/rand/owner_user'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - users: [], - }); - }); - - test('returns the list of users that own the specified crate', async function (assert) { - let user = this.server.create('user', { name: 'John Doe' }); - let crate = this.server.create('crate', { name: 'rand' }); - this.server.create('crate-ownership', { crate, user }); - - let response = await fetch('/api/v1/crates/rand/owner_user'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - users: [ - { - id: 1, - avatar: 'https://avatars1.githubusercontent.com/u/14631425?v=4', - kind: 'user', - login: 'john-doe', - name: 'John Doe', - url: 'https://github.com/john-doe', - }, - ], - }); - }); - }); - - module('GET /api/v1/crates/:id/owner_team', function () { - test('returns 404 for unknown crates', async function (assert) { - let response = await fetch('/api/v1/crates/foo/owner_team'); - assert.equal(response.status, 404); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { errors: [{ detail: 'Not Found' }] }); - }); - - test('empty case', async function (assert) { - this.server.create('crate', { name: 'rand' }); - - let response = await fetch('/api/v1/crates/rand/owner_team'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - teams: [], - }); - }); - - test('returns the list of teams that own the specified crate', async function (assert) { - let team = this.server.create('team', { name: 'maintainers' }); - let crate = this.server.create('crate', { name: 'rand' }); - this.server.create('crate-ownership', { crate, team }); - - let response = await fetch('/api/v1/crates/rand/owner_team'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - teams: [ - { - id: 1, - avatar: 'https://avatars1.githubusercontent.com/u/14631425?v=4', - kind: 'team', - login: 'github:rust-lang:maintainers', - name: 'maintainers', - url: 'https://github.com/rust-lang', - }, - ], - }); - }); - }); - - module('GET /api/v1/crates/:id/reverse_dependencies', function () { - test('returns 404 for unknown crates', async function (assert) { - let response = await fetch('/api/v1/crates/foo/reverse_dependencies'); - assert.equal(response.status, 404); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { errors: [{ detail: 'Not Found' }] }); - }); - - test('empty case', async function (assert) { - this.server.create('crate', { name: 'rand' }); - - let response = await fetch('/api/v1/crates/rand/reverse_dependencies'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - dependencies: [], - versions: [], - meta: { - total: 0, - }, - }); - }); - - test('returns a paginated list of crate versions depending to the specified crate', async function (assert) { - let crate = this.server.create('crate', { name: 'foo' }); - - this.server.create('dependency', { - crate, - version: this.server.create('version', { - crate: this.server.create('crate', { name: 'bar' }), - }), - }); - - this.server.create('dependency', { - crate, - version: this.server.create('version', { - crate: this.server.create('crate', { name: 'baz' }), - }), - }); - - let response = await fetch('/api/v1/crates/foo/reverse_dependencies'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - dependencies: [ - { - id: '1', - crate_id: 'foo', - default_features: false, - features: [], - kind: 'dev', - optional: true, - req: '^0.1.0', - target: null, - version_id: '1', - }, - { - id: '2', - crate_id: 'foo', - default_features: false, - features: [], - kind: 'normal', - optional: true, - req: '^2.1.3', - target: null, - version_id: '2', - }, - ], - versions: [ - { - id: '1', - crate: 'bar', - crate_size: 0, - created_at: '2010-06-16T21:30:45Z', - dl_path: '/api/v1/crates/bar/1.0.0/download', - downloads: 0, - license: 'MIT/Apache-2.0', - links: { - dependencies: '/api/v1/crates/bar/1.0.0/dependencies', - version_downloads: '/api/v1/crates/bar/1.0.0/downloads', - }, - num: '1.0.0', - published_by: null, - updated_at: '2017-02-24T12:34:56Z', - yanked: false, - }, - { - id: '2', - crate: 'baz', - crate_size: 162_963, - created_at: '2010-06-16T21:30:45Z', - dl_path: '/api/v1/crates/baz/1.0.1/download', - downloads: 3702, - license: 'MIT', - links: { - dependencies: '/api/v1/crates/baz/1.0.1/dependencies', - version_downloads: '/api/v1/crates/baz/1.0.1/downloads', - }, - num: '1.0.1', - published_by: null, - updated_at: '2017-02-24T12:34:56Z', - yanked: false, - }, - ], - meta: { - total: 2, - }, - }); - }); - - test('never returns more than 10 results', async function (assert) { - let crate = this.server.create('crate', { name: 'foo' }); - - this.server.createList('dependency', 25, { - crate, - version: () => - this.server.create('version', { - crate: () => this.server.create('crate', { name: 'bar' }), - }), - }); - - let response = await fetch('/api/v1/crates/foo/reverse_dependencies'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.equal(responsePayload.dependencies.length, 10); - assert.equal(responsePayload.versions.length, 10); - assert.equal(responsePayload.meta.total, 25); - }); - - test('supports `page` and `per_page` parameters', async function (assert) { - let crate = this.server.create('crate', { name: 'foo' }); - - let crates = this.server.createList('crate', 25, { - name: i => `crate-${String(i + 1).padStart(2, '0')}`, - }); - let versions = this.server.createList('version', crates.length, { - crate: i => crates[i], - }); - this.server.createList('dependency', versions.length, { - crate, - versionId: i => versions[i].id, - }); - - let response = await fetch('/api/v1/crates/foo/reverse_dependencies?page=2&per_page=5'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.equal(responsePayload.dependencies.length, 5); - assert.deepEqual( - responsePayload.versions.map(it => it.crate), - // offset by one because we created the `foo` crate first - ['crate-07', 'crate-08', 'crate-09', 'crate-10', 'crate-11'], - ); - assert.equal(responsePayload.meta.total, 25); - }); - }); - - module('GET /api/v1/crates/:id/downloads', function () { - test('returns 404 for unknown crates', async function (assert) { - let response = await fetch('/api/v1/crates/foo/downloads'); - assert.equal(response.status, 404); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { errors: [{ detail: 'Not Found' }] }); - }); - - test('empty case', async function (assert) { - this.server.create('crate', { name: 'rand' }); - - let response = await fetch('/api/v1/crates/rand/downloads'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - version_downloads: [], - meta: { - extra_downloads: [], - }, - }); - }); - - test('returns a list of version downloads belonging to the specified crate version', async function (assert) { - let crate = this.server.create('crate', { name: 'rand' }); - let versions = this.server.createList('version', 2, { crate }); - this.server.create('version-download', { version: versions[0], date: '2020-01-13' }); - this.server.create('version-download', { version: versions[1], date: '2020-01-14' }); - this.server.create('version-download', { version: versions[1], date: '2020-01-15' }); - - let response = await fetch('/api/v1/crates/rand/downloads'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - version_downloads: [ - { - date: '2020-01-13', - downloads: 9380, - version: '1', - }, - { - date: '2020-01-14', - downloads: 16_415, - version: '2', - }, - { - date: '2020-01-15', - downloads: 23_450, - version: '2', - }, - ], - meta: { - extra_downloads: [], - }, - }); - }); - }); -}); diff --git a/tests/mirage/crates/downloads-test.js b/tests/mirage/crates/downloads-test.js new file mode 100644 index 00000000000..75de1660853 --- /dev/null +++ b/tests/mirage/crates/downloads-test.js @@ -0,0 +1,63 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; + +import { setupTest } from '../../helpers'; +import setupMirage from '../../helpers/setup-mirage'; + +module('Mirage | GET /api/v1/crates/:id/downloads', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('returns 404 for unknown crates', async function (assert) { + let response = await fetch('/api/v1/crates/foo/downloads'); + assert.equal(response.status, 404); + assert.deepEqual(await response.json(), { errors: [{ detail: 'Not Found' }] }); + }); + + test('empty case', async function (assert) { + this.server.create('crate', { name: 'rand' }); + + let response = await fetch('/api/v1/crates/rand/downloads'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + version_downloads: [], + meta: { + extra_downloads: [], + }, + }); + }); + + test('returns a list of version downloads belonging to the specified crate version', async function (assert) { + let crate = this.server.create('crate', { name: 'rand' }); + let versions = this.server.createList('version', 2, { crate }); + this.server.create('version-download', { version: versions[0], date: '2020-01-13' }); + this.server.create('version-download', { version: versions[1], date: '2020-01-14' }); + this.server.create('version-download', { version: versions[1], date: '2020-01-15' }); + + let response = await fetch('/api/v1/crates/rand/downloads'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + version_downloads: [ + { + date: '2020-01-13', + downloads: 9380, + version: '1', + }, + { + date: '2020-01-14', + downloads: 16_415, + version: '2', + }, + { + date: '2020-01-15', + downloads: 23_450, + version: '2', + }, + ], + meta: { + extra_downloads: [], + }, + }); + }); +}); diff --git a/tests/mirage/crates/follow/delete-test.js b/tests/mirage/crates/follow/delete-test.js new file mode 100644 index 00000000000..de4054bb98f --- /dev/null +++ b/tests/mirage/crates/follow/delete-test.js @@ -0,0 +1,44 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; + +import { setupTest } from '../../../helpers'; +import setupMirage from '../../../helpers/setup-mirage'; + +module('Mirage | DELETE /api/v1/crates/:crateId/follow', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('returns 403 if unauthenticated', async function (assert) { + let response = await fetch('/api/v1/crates/foo/follow', { method: 'DELETE' }); + assert.equal(response.status, 403); + assert.deepEqual(await response.json(), { + errors: [{ detail: 'must be logged in to perform that action' }], + }); + }); + + test('returns 404 for unknown crates', async function (assert) { + let user = this.server.create('user'); + this.authenticateAs(user); + + let response = await fetch('/api/v1/crates/foo/follow', { method: 'DELETE' }); + assert.equal(response.status, 404); + assert.deepEqual(await response.json(), { errors: [{ detail: 'Not Found' }] }); + }); + + test('makes the authenticated user unfollow the crate', async function (assert) { + let crate = this.server.create('crate', { name: 'rand' }); + + let user = this.server.create('user', { followedCrates: [crate] }); + this.authenticateAs(user); + + assert.deepEqual(user.followedCrateIds, [crate.id]); + + let response = await fetch('/api/v1/crates/rand/follow', { method: 'DELETE' }); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { ok: true }); + + user.reload(); + assert.deepEqual(user.followedCrateIds, []); + }); +}); diff --git a/tests/mirage/crates/follow/get-test.js b/tests/mirage/crates/follow/get-test.js new file mode 100644 index 00000000000..921ffe7e306 --- /dev/null +++ b/tests/mirage/crates/follow/get-test.js @@ -0,0 +1,50 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; + +import { setupTest } from '../../../helpers'; +import setupMirage from '../../../helpers/setup-mirage'; + +module('Mirage | GET /api/v1/crates/:crateId/following', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('returns 403 if unauthenticated', async function (assert) { + let response = await fetch('/api/v1/crates/foo/following'); + assert.equal(response.status, 403); + assert.deepEqual(await response.json(), { + errors: [{ detail: 'must be logged in to perform that action' }], + }); + }); + + test('returns 404 for unknown crates', async function (assert) { + let user = this.server.create('user'); + this.authenticateAs(user); + + let response = await fetch('/api/v1/crates/foo/following'); + assert.equal(response.status, 404); + assert.deepEqual(await response.json(), { errors: [{ detail: 'Not Found' }] }); + }); + + test('returns true if the authenticated user follows the crate', async function (assert) { + let crate = this.server.create('crate', { name: 'rand' }); + + let user = this.server.create('user', { followedCrates: [crate] }); + this.authenticateAs(user); + + let response = await fetch('/api/v1/crates/rand/following'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { following: true }); + }); + + test('returns false if the authenticated user is not following the crate', async function (assert) { + this.server.create('crate', { name: 'rand' }); + + let user = this.server.create('user'); + this.authenticateAs(user); + + let response = await fetch('/api/v1/crates/rand/following'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { following: false }); + }); +}); diff --git a/tests/mirage/crates/follow/put-test.js b/tests/mirage/crates/follow/put-test.js new file mode 100644 index 00000000000..b677fb19af3 --- /dev/null +++ b/tests/mirage/crates/follow/put-test.js @@ -0,0 +1,44 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; + +import { setupTest } from '../../../helpers'; +import setupMirage from '../../../helpers/setup-mirage'; + +module('Mirage | PUT /api/v1/crates/:crateId/follow', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('returns 403 if unauthenticated', async function (assert) { + let response = await fetch('/api/v1/crates/foo/follow', { method: 'PUT' }); + assert.equal(response.status, 403); + assert.deepEqual(await response.json(), { + errors: [{ detail: 'must be logged in to perform that action' }], + }); + }); + + test('returns 404 for unknown crates', async function (assert) { + let user = this.server.create('user'); + this.authenticateAs(user); + + let response = await fetch('/api/v1/crates/foo/follow', { method: 'PUT' }); + assert.equal(response.status, 404); + assert.deepEqual(await response.json(), { errors: [{ detail: 'Not Found' }] }); + }); + + test('makes the authenticated user follow the crate', async function (assert) { + let crate = this.server.create('crate', { name: 'rand' }); + + let user = this.server.create('user'); + this.authenticateAs(user); + + assert.deepEqual(user.followedCrateIds, []); + + let response = await fetch('/api/v1/crates/rand/follow', { method: 'PUT' }); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { ok: true }); + + user.reload(); + assert.deepEqual(user.followedCrateIds, [crate.id]); + }); +}); diff --git a/tests/mirage/crates/get-by-id-test.js b/tests/mirage/crates/get-by-id-test.js new file mode 100644 index 00000000000..255dfccfb3b --- /dev/null +++ b/tests/mirage/crates/get-by-id-test.js @@ -0,0 +1,182 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; + +import { setupTest } from '../../helpers'; +import setupMirage from '../../helpers/setup-mirage'; + +module('Mirage | GET /api/v1/crates/:id', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('returns 404 for unknown crates', async function (assert) { + let response = await fetch('/api/v1/crates/foo'); + assert.equal(response.status, 404); + assert.deepEqual(await response.json(), { errors: [{ detail: 'Not Found' }] }); + }); + + test('returns a crate object for known crates', async function (assert) { + let crate = this.server.create('crate', { name: 'rand' }); + this.server.create('version', { crate, num: '1.0.0-beta.1' }); + + let response = await fetch('/api/v1/crates/rand'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + categories: [], + crate: { + badges: [], + categories: [], + created_at: '2010-06-16T21:30:45Z', + description: 'This is the description for the crate called "rand"', + documentation: null, + downloads: 0, + homepage: null, + id: 'rand', + keywords: [], + links: { + owner_team: '/api/v1/crates/rand/owner_team', + owner_user: '/api/v1/crates/rand/owner_user', + reverse_dependencies: '/api/v1/crates/rand/reverse_dependencies', + version_downloads: '/api/v1/crates/rand/downloads', + versions: '/api/v1/crates/rand/versions', + }, + max_version: '1.0.0-beta.1', + max_stable_version: null, + name: 'rand', + newest_version: '1.0.0-beta.1', + repository: null, + updated_at: '2017-02-24T12:34:56Z', + versions: ['1'], + }, + keywords: [], + versions: [ + { + id: '1', + crate: 'rand', + crate_size: 0, + created_at: '2010-06-16T21:30:45Z', + dl_path: '/api/v1/crates/rand/1.0.0-beta.1/download', + downloads: 0, + license: 'MIT/Apache-2.0', + links: { + dependencies: '/api/v1/crates/rand/1.0.0-beta.1/dependencies', + version_downloads: '/api/v1/crates/rand/1.0.0-beta.1/downloads', + }, + num: '1.0.0-beta.1', + published_by: null, + updated_at: '2017-02-24T12:34:56Z', + yanked: false, + }, + ], + }); + }); + + test('includes related versions', async function (assert) { + let crate = this.server.create('crate', { name: 'rand' }); + this.server.create('version', { crate, num: '1.0.0' }); + this.server.create('version', { crate, num: '1.1.0' }); + this.server.create('version', { crate, num: '1.2.0' }); + + let response = await fetch('/api/v1/crates/rand'); + assert.equal(response.status, 200); + + let responsePayload = await response.json(); + assert.deepEqual(responsePayload.crate.versions, ['1', '2', '3']); + assert.deepEqual(responsePayload.versions, [ + { + id: '1', + crate: 'rand', + crate_size: 0, + created_at: '2010-06-16T21:30:45Z', + dl_path: '/api/v1/crates/rand/1.0.0/download', + downloads: 0, + license: 'MIT/Apache-2.0', + links: { + dependencies: '/api/v1/crates/rand/1.0.0/dependencies', + version_downloads: '/api/v1/crates/rand/1.0.0/downloads', + }, + num: '1.0.0', + published_by: null, + updated_at: '2017-02-24T12:34:56Z', + yanked: false, + }, + { + id: '2', + crate: 'rand', + crate_size: 162_963, + created_at: '2010-06-16T21:30:45Z', + dl_path: '/api/v1/crates/rand/1.1.0/download', + downloads: 3702, + license: 'MIT', + links: { + dependencies: '/api/v1/crates/rand/1.1.0/dependencies', + version_downloads: '/api/v1/crates/rand/1.1.0/downloads', + }, + num: '1.1.0', + published_by: null, + updated_at: '2017-02-24T12:34:56Z', + yanked: false, + }, + { + id: '3', + crate: 'rand', + crate_size: 325_926, + created_at: '2010-06-16T21:30:45Z', + dl_path: '/api/v1/crates/rand/1.2.0/download', + downloads: 7404, + license: 'Apache-2.0', + links: { + dependencies: '/api/v1/crates/rand/1.2.0/dependencies', + version_downloads: '/api/v1/crates/rand/1.2.0/downloads', + }, + num: '1.2.0', + published_by: null, + updated_at: '2017-02-24T12:34:56Z', + yanked: false, + }, + ]); + }); + + test('includes related categories', async function (assert) { + this.server.create('category', { category: 'no-std' }); + this.server.create('category', { category: 'cli' }); + let crate = this.server.create('crate', { name: 'rand', categoryIds: ['no-std'] }); + this.server.create('version', { crate }); + + let response = await fetch('/api/v1/crates/rand'); + assert.equal(response.status, 200); + + let responsePayload = await response.json(); + assert.deepEqual(responsePayload.crate.categories, ['no-std']); + assert.deepEqual(responsePayload.categories, [ + { + id: 'no-std', + category: 'no-std', + crates_cnt: 1, + created_at: '2010-06-16T21:30:45Z', + description: 'This is the description for the category called "no-std"', + slug: 'no-std', + }, + ]); + }); + + test('includes related keywords', async function (assert) { + this.server.create('keyword', { keyword: 'no-std' }); + this.server.create('keyword', { keyword: 'cli' }); + let crate = this.server.create('crate', { name: 'rand', keywordIds: ['no-std'] }); + this.server.create('version', { crate }); + + let response = await fetch('/api/v1/crates/rand'); + assert.equal(response.status, 200); + + let responsePayload = await response.json(); + assert.deepEqual(responsePayload.crate.keywords, ['no-std']); + assert.deepEqual(responsePayload.keywords, [ + { + crates_cnt: 1, + id: 'no-std', + keyword: 'no-std', + }, + ]); + }); +}); diff --git a/tests/mirage/crates/list-test.js b/tests/mirage/crates/list-test.js new file mode 100644 index 00000000000..513fb894719 --- /dev/null +++ b/tests/mirage/crates/list-test.js @@ -0,0 +1,226 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; + +import { setupTest } from '../../helpers'; +import setupMirage from '../../helpers/setup-mirage'; + +module('Mirage | GET /api/v1/crates', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('empty case', async function (assert) { + let response = await fetch('/api/v1/crates'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + crates: [], + meta: { + total: 0, + }, + }); + }); + + test('returns a paginated crates list', async function (assert) { + let crate = this.server.create('crate', { name: 'rand' }); + this.server.create('version', { + crate, + created_at: '2020-11-06T12:34:56Z', + num: '1.0.0', + updated_at: '2020-11-06T12:34:56Z', + }); + this.server.create('version', { + crate, + created_at: '2020-12-25T12:34:56Z', + num: '2.0.0-beta.1', + updated_at: '2020-12-25T12:34:56Z', + }); + + let response = await fetch('/api/v1/crates'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + crates: [ + { + id: 'rand', + badges: [], + categories: [], + created_at: '2010-06-16T21:30:45Z', + description: 'This is the description for the crate called "rand"', + documentation: null, + downloads: 0, + homepage: null, + keywords: [], + links: { + owner_team: '/api/v1/crates/rand/owner_team', + owner_user: '/api/v1/crates/rand/owner_user', + reverse_dependencies: '/api/v1/crates/rand/reverse_dependencies', + version_downloads: '/api/v1/crates/rand/downloads', + versions: '/api/v1/crates/rand/versions', + }, + max_version: '2.0.0-beta.1', + max_stable_version: '1.0.0', + name: 'rand', + newest_version: '2.0.0-beta.1', + repository: null, + updated_at: '2017-02-24T12:34:56Z', + versions: ['1', '2'], + }, + ], + meta: { + total: 1, + }, + }); + }); + + test('never returns more than 10 results', async function (assert) { + let crates = this.server.createList('crate', 25); + this.server.createList('version', crates.length, { crate: i => crates[i] }); + + let response = await fetch('/api/v1/crates'); + assert.equal(response.status, 200); + + let responsePayload = await response.json(); + assert.equal(responsePayload.crates.length, 10); + assert.equal(responsePayload.meta.total, 25); + }); + + test('supports `page` and `per_page` parameters', async function (assert) { + let crates = this.server.createList('crate', 25, { + name: i => `crate-${String(i + 1).padStart(2, '0')}`, + }); + this.server.createList('version', crates.length, { crate: i => crates[i] }); + + let response = await fetch('/api/v1/crates?page=2&per_page=5'); + assert.equal(response.status, 200); + + let responsePayload = await response.json(); + assert.equal(responsePayload.crates.length, 5); + assert.deepEqual( + responsePayload.crates.map(it => it.id), + ['crate-06', 'crate-07', 'crate-08', 'crate-09', 'crate-10'], + ); + assert.equal(responsePayload.meta.total, 25); + }); + + test('supports a `letter` parameter', async function (assert) { + let foo = this.server.create('crate', { name: 'foo' }); + this.server.create('version', { crate: foo }); + let bar = this.server.create('crate', { name: 'bar' }); + this.server.create('version', { crate: bar }); + let baz = this.server.create('crate', { name: 'BAZ' }); + this.server.create('version', { crate: baz }); + + let response = await fetch('/api/v1/crates?letter=b'); + assert.equal(response.status, 200); + + let responsePayload = await response.json(); + assert.equal(responsePayload.crates.length, 2); + assert.deepEqual( + responsePayload.crates.map(it => it.id), + ['bar', 'BAZ'], + ); + assert.equal(responsePayload.meta.total, 2); + }); + + test('supports a `q` parameter', async function (assert) { + let crate1 = this.server.create('crate', { name: '123456' }); + this.server.create('version', { crate: crate1 }); + let crate2 = this.server.create('crate', { name: '00123' }); + this.server.create('version', { crate: crate2 }); + let crate3 = this.server.create('crate', { name: '87654' }); + this.server.create('version', { crate: crate3 }); + + let response = await fetch('/api/v1/crates?q=123'); + assert.equal(response.status, 200); + + let responsePayload = await response.json(); + assert.equal(responsePayload.crates.length, 2); + assert.deepEqual( + responsePayload.crates.map(it => it.id), + ['123456', '00123'], + ); + assert.equal(responsePayload.meta.total, 2); + }); + + test('supports a `user_id` parameter', async function (assert) { + let user1 = this.server.create('user'); + let user2 = this.server.create('user'); + + let foo = this.server.create('crate', { name: 'foo' }); + this.server.create('version', { crate: foo }); + let bar = this.server.create('crate', { name: 'bar' }); + this.server.create('crate-ownership', { crate: bar, user: user1 }); + this.server.create('version', { crate: bar }); + let baz = this.server.create('crate', { name: 'baz' }); + this.server.create('crate-ownership', { crate: baz, user: user2 }); + this.server.create('version', { crate: baz }); + + let response = await fetch(`/api/v1/crates?user_id=${user1.id}`); + assert.equal(response.status, 200); + + let responsePayload = await response.json(); + assert.equal(responsePayload.crates.length, 1); + assert.equal(responsePayload.crates[0].id, 'bar'); + assert.equal(responsePayload.meta.total, 1); + }); + + test('supports a `team_id` parameter', async function (assert) { + let team1 = this.server.create('team'); + let team2 = this.server.create('team'); + + let foo = this.server.create('crate', { name: 'foo' }); + this.server.create('version', { crate: foo }); + let bar = this.server.create('crate', { name: 'bar' }); + this.server.create('crate-ownership', { crate: bar, team: team1 }); + this.server.create('version', { crate: bar }); + let baz = this.server.create('crate', { name: 'baz' }); + this.server.create('crate-ownership', { crate: baz, team: team2 }); + this.server.create('version', { crate: baz }); + + let response = await fetch(`/api/v1/crates?team_id=${team1.id}`); + assert.equal(response.status, 200); + + let responsePayload = await response.json(); + assert.equal(responsePayload.crates.length, 1); + assert.equal(responsePayload.crates[0].id, 'bar'); + assert.equal(responsePayload.meta.total, 1); + }); + + test('supports a `following` parameter', async function (assert) { + let foo = this.server.create('crate', { name: 'foo' }); + this.server.create('version', { crate: foo }); + let bar = this.server.create('crate', { name: 'bar' }); + this.server.create('version', { crate: bar }); + + let user = this.server.create('user', { followedCrates: [bar] }); + this.authenticateAs(user); + + let response = await fetch(`/api/v1/crates?following=1`); + assert.equal(response.status, 200); + + let responsePayload = await response.json(); + assert.equal(responsePayload.crates.length, 1); + assert.equal(responsePayload.crates[0].id, 'bar'); + assert.equal(responsePayload.meta.total, 1); + }); + + test('supports multiple `ids[]` parameters', async function (assert) { + let foo = this.server.create('crate', { name: 'foo' }); + this.server.create('version', { crate: foo }); + let bar = this.server.create('crate', { name: 'bar' }); + this.server.create('version', { crate: bar }); + let baz = this.server.create('crate', { name: 'baz' }); + this.server.create('version', { crate: baz }); + let other = this.server.create('crate', { name: 'other' }); + this.server.create('version', { crate: other }); + + let response = await fetch(`/api/v1/crates?ids[]=foo&ids[]=bar&ids[]=baz&ids[]=baz&ids[]=unknown`); + assert.equal(response.status, 200); + + let responsePayload = await response.json(); + assert.equal(responsePayload.crates.length, 3); + assert.equal(responsePayload.crates[0].id, 'foo'); + assert.equal(responsePayload.crates[1].id, 'bar'); + assert.equal(responsePayload.crates[2].id, 'baz'); + assert.equal(responsePayload.meta.total, 3); + }); +}); diff --git a/tests/mirage/crates/owner-team-test.js b/tests/mirage/crates/owner-team-test.js new file mode 100644 index 00000000000..6df05c4f479 --- /dev/null +++ b/tests/mirage/crates/owner-team-test.js @@ -0,0 +1,48 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; + +import { setupTest } from '../../helpers'; +import setupMirage from '../../helpers/setup-mirage'; + +module('Mirage | GET /api/v1/crates/:id/owner_team', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('returns 404 for unknown crates', async function (assert) { + let response = await fetch('/api/v1/crates/foo/owner_team'); + assert.equal(response.status, 404); + assert.deepEqual(await response.json(), { errors: [{ detail: 'Not Found' }] }); + }); + + test('empty case', async function (assert) { + this.server.create('crate', { name: 'rand' }); + + let response = await fetch('/api/v1/crates/rand/owner_team'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + teams: [], + }); + }); + + test('returns the list of teams that own the specified crate', async function (assert) { + let team = this.server.create('team', { name: 'maintainers' }); + let crate = this.server.create('crate', { name: 'rand' }); + this.server.create('crate-ownership', { crate, team }); + + let response = await fetch('/api/v1/crates/rand/owner_team'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + teams: [ + { + id: 1, + avatar: 'https://avatars1.githubusercontent.com/u/14631425?v=4', + kind: 'team', + login: 'github:rust-lang:maintainers', + name: 'maintainers', + url: 'https://github.com/rust-lang', + }, + ], + }); + }); +}); diff --git a/tests/mirage/crates/owner-user-test.js b/tests/mirage/crates/owner-user-test.js new file mode 100644 index 00000000000..44d434cc367 --- /dev/null +++ b/tests/mirage/crates/owner-user-test.js @@ -0,0 +1,48 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; + +import { setupTest } from '../../helpers'; +import setupMirage from '../../helpers/setup-mirage'; + +module('Mirage | GET /api/v1/crates/:id/owner_user', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('returns 404 for unknown crates', async function (assert) { + let response = await fetch('/api/v1/crates/foo/owner_user'); + assert.equal(response.status, 404); + assert.deepEqual(await response.json(), { errors: [{ detail: 'Not Found' }] }); + }); + + test('empty case', async function (assert) { + this.server.create('crate', { name: 'rand' }); + + let response = await fetch('/api/v1/crates/rand/owner_user'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + users: [], + }); + }); + + test('returns the list of users that own the specified crate', async function (assert) { + let user = this.server.create('user', { name: 'John Doe' }); + let crate = this.server.create('crate', { name: 'rand' }); + this.server.create('crate-ownership', { crate, user }); + + let response = await fetch('/api/v1/crates/rand/owner_user'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + users: [ + { + id: 1, + avatar: 'https://avatars1.githubusercontent.com/u/14631425?v=4', + kind: 'user', + login: 'john-doe', + name: 'John Doe', + url: 'https://github.com/john-doe', + }, + ], + }); + }); +}); diff --git a/tests/mirage/crates/reverse-dependencies-test.js b/tests/mirage/crates/reverse-dependencies-test.js new file mode 100644 index 00000000000..445c2e44ce4 --- /dev/null +++ b/tests/mirage/crates/reverse-dependencies-test.js @@ -0,0 +1,164 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; + +import { setupTest } from '../../helpers'; +import setupMirage from '../../helpers/setup-mirage'; + +module('Mirage | GET /api/v1/crates/:id/reverse_dependencies', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('returns 404 for unknown crates', async function (assert) { + let response = await fetch('/api/v1/crates/foo/reverse_dependencies'); + assert.equal(response.status, 404); + assert.deepEqual(await response.json(), { errors: [{ detail: 'Not Found' }] }); + }); + + test('empty case', async function (assert) { + this.server.create('crate', { name: 'rand' }); + + let response = await fetch('/api/v1/crates/rand/reverse_dependencies'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + dependencies: [], + versions: [], + meta: { + total: 0, + }, + }); + }); + + test('returns a paginated list of crate versions depending to the specified crate', async function (assert) { + let crate = this.server.create('crate', { name: 'foo' }); + + this.server.create('dependency', { + crate, + version: this.server.create('version', { + crate: this.server.create('crate', { name: 'bar' }), + }), + }); + + this.server.create('dependency', { + crate, + version: this.server.create('version', { + crate: this.server.create('crate', { name: 'baz' }), + }), + }); + + let response = await fetch('/api/v1/crates/foo/reverse_dependencies'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + dependencies: [ + { + id: '1', + crate_id: 'foo', + default_features: false, + features: [], + kind: 'dev', + optional: true, + req: '^0.1.0', + target: null, + version_id: '1', + }, + { + id: '2', + crate_id: 'foo', + default_features: false, + features: [], + kind: 'normal', + optional: true, + req: '^2.1.3', + target: null, + version_id: '2', + }, + ], + versions: [ + { + id: '1', + crate: 'bar', + crate_size: 0, + created_at: '2010-06-16T21:30:45Z', + dl_path: '/api/v1/crates/bar/1.0.0/download', + downloads: 0, + license: 'MIT/Apache-2.0', + links: { + dependencies: '/api/v1/crates/bar/1.0.0/dependencies', + version_downloads: '/api/v1/crates/bar/1.0.0/downloads', + }, + num: '1.0.0', + published_by: null, + updated_at: '2017-02-24T12:34:56Z', + yanked: false, + }, + { + id: '2', + crate: 'baz', + crate_size: 162_963, + created_at: '2010-06-16T21:30:45Z', + dl_path: '/api/v1/crates/baz/1.0.1/download', + downloads: 3702, + license: 'MIT', + links: { + dependencies: '/api/v1/crates/baz/1.0.1/dependencies', + version_downloads: '/api/v1/crates/baz/1.0.1/downloads', + }, + num: '1.0.1', + published_by: null, + updated_at: '2017-02-24T12:34:56Z', + yanked: false, + }, + ], + meta: { + total: 2, + }, + }); + }); + + test('never returns more than 10 results', async function (assert) { + let crate = this.server.create('crate', { name: 'foo' }); + + this.server.createList('dependency', 25, { + crate, + version: () => + this.server.create('version', { + crate: () => this.server.create('crate', { name: 'bar' }), + }), + }); + + let response = await fetch('/api/v1/crates/foo/reverse_dependencies'); + assert.equal(response.status, 200); + + let responsePayload = await response.json(); + assert.equal(responsePayload.dependencies.length, 10); + assert.equal(responsePayload.versions.length, 10); + assert.equal(responsePayload.meta.total, 25); + }); + + test('supports `page` and `per_page` parameters', async function (assert) { + let crate = this.server.create('crate', { name: 'foo' }); + + let crates = this.server.createList('crate', 25, { + name: i => `crate-${String(i + 1).padStart(2, '0')}`, + }); + let versions = this.server.createList('version', crates.length, { + crate: i => crates[i], + }); + this.server.createList('dependency', versions.length, { + crate, + versionId: i => versions[i].id, + }); + + let response = await fetch('/api/v1/crates/foo/reverse_dependencies?page=2&per_page=5'); + assert.equal(response.status, 200); + + let responsePayload = await response.json(); + assert.equal(responsePayload.dependencies.length, 5); + assert.deepEqual( + responsePayload.versions.map(it => it.crate), + // offset by one because we created the `foo` crate first + ['crate-07', 'crate-08', 'crate-09', 'crate-10', 'crate-11'], + ); + assert.equal(responsePayload.meta.total, 25); + }); +}); diff --git a/tests/mirage/crates/versions/authors-test.js b/tests/mirage/crates/versions/authors-test.js new file mode 100644 index 00000000000..f01f72ce514 --- /dev/null +++ b/tests/mirage/crates/versions/authors-test.js @@ -0,0 +1,55 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; + +import { setupTest } from '../../../helpers'; +import setupMirage from '../../../helpers/setup-mirage'; + +module('Mirage | GET /api/v1/crates/:id/:version/authors', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('returns 404 for unknown crates', async function (assert) { + let response = await fetch('/api/v1/crates/foo/1.0.0/authors'); + assert.equal(response.status, 404); + assert.deepEqual(await response.json(), { errors: [{ detail: 'Not Found' }] }); + }); + + test('returns 200 for unknown versions', async function (assert) { + this.server.create('crate', { name: 'rand' }); + + let response = await fetch('/api/v1/crates/rand/1.0.0/authors'); + // we should probably return 404 for this, but the production API + // currently doesn't do this either + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { errors: [{ detail: 'crate `rand` does not have a version `1.0.0`' }] }); + }); + + test('empty case', async function (assert) { + let crate = this.server.create('crate', { name: 'rand' }); + this.server.create('version', { crate, num: '1.0.0' }); + + let response = await fetch('/api/v1/crates/rand/1.0.0/authors'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + meta: { + names: [], + }, + users: [], + }); + }); + + test('returns a list of authors belonging to the specified crate version', async function (assert) { + let crate = this.server.create('crate', { name: 'rand' }); + this.server.create('version', { crate, num: '1.0.0' }); + + let response = await fetch('/api/v1/crates/rand/1.0.0/authors'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + meta: { + names: [], + }, + users: [], + }); + }); +}); diff --git a/tests/mirage/crates/versions/dependencies-test.js b/tests/mirage/crates/versions/dependencies-test.js new file mode 100644 index 00000000000..7f4f4f6cc0b --- /dev/null +++ b/tests/mirage/crates/versions/dependencies-test.js @@ -0,0 +1,90 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; + +import { setupTest } from '../../../helpers'; +import setupMirage from '../../../helpers/setup-mirage'; + +module('Mirage | GET /api/v1/crates/:id/:version/dependencies', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('returns 404 for unknown crates', async function (assert) { + let response = await fetch('/api/v1/crates/foo/1.0.0/dependencies'); + assert.equal(response.status, 404); + assert.deepEqual(await response.json(), { errors: [{ detail: 'Not Found' }] }); + }); + + test('returns 200 for unknown versions', async function (assert) { + this.server.create('crate', { name: 'rand' }); + + let response = await fetch('/api/v1/crates/rand/1.0.0/dependencies'); + // we should probably return 404 for this, but the production API + // currently doesn't do this either + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { errors: [{ detail: 'crate `rand` does not have a version `1.0.0`' }] }); + }); + + test('empty case', async function (assert) { + let crate = this.server.create('crate', { name: 'rand' }); + this.server.create('version', { crate, num: '1.0.0' }); + + let response = await fetch('/api/v1/crates/rand/1.0.0/dependencies'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + dependencies: [], + }); + }); + + test('returns a list of dependencies belonging to the specified crate version', async function (assert) { + let crate = this.server.create('crate', { name: 'rand' }); + let version = this.server.create('version', { crate, num: '1.0.0' }); + + let foo = this.server.create('crate', { name: 'foo' }); + this.server.create('dependency', { crate: foo, version }); + let bar = this.server.create('crate', { name: 'bar' }); + this.server.create('dependency', { crate: bar, version }); + let baz = this.server.create('crate', { name: 'baz' }); + this.server.create('dependency', { crate: baz, version }); + + let response = await fetch('/api/v1/crates/rand/1.0.0/dependencies'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + dependencies: [ + { + id: '1', + crate_id: 'foo', + default_features: false, + features: [], + kind: 'dev', + optional: true, + req: '^0.1.0', + target: null, + version_id: '1', + }, + { + id: '2', + crate_id: 'bar', + default_features: false, + features: [], + kind: 'normal', + optional: true, + req: '^2.1.3', + target: null, + version_id: '1', + }, + { + id: '3', + crate_id: 'baz', + default_features: false, + features: [], + kind: 'normal', + optional: true, + req: '0.3.7', + target: null, + version_id: '1', + }, + ], + }); + }); +}); diff --git a/tests/mirage/crates/versions/downloads-test.js b/tests/mirage/crates/versions/downloads-test.js new file mode 100644 index 00000000000..71961352c3f --- /dev/null +++ b/tests/mirage/crates/versions/downloads-test.js @@ -0,0 +1,68 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; + +import { setupTest } from '../../../helpers'; +import setupMirage from '../../../helpers/setup-mirage'; + +module('Mirage | GET /api/v1/crates/:id/:version/downloads', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('returns 404 for unknown crates', async function (assert) { + let response = await fetch('/api/v1/crates/foo/1.0.0/downloads'); + assert.equal(response.status, 404); + assert.deepEqual(await response.json(), { errors: [{ detail: 'Not Found' }] }); + }); + + test('returns 200 for unknown versions', async function (assert) { + this.server.create('crate', { name: 'rand' }); + + let response = await fetch('/api/v1/crates/rand/1.0.0/downloads'); + // we should probably return 404 for this, but the production API + // currently doesn't do this either + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { errors: [{ detail: 'crate `rand` does not have a version `1.0.0`' }] }); + }); + + test('empty case', async function (assert) { + let crate = this.server.create('crate', { name: 'rand' }); + this.server.create('version', { crate, num: '1.0.0' }); + + let response = await fetch('/api/v1/crates/rand/1.0.0/downloads'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + version_downloads: [], + }); + }); + + test('returns a list of version downloads belonging to the specified crate version', async function (assert) { + let crate = this.server.create('crate', { name: 'rand' }); + let version = this.server.create('version', { crate, num: '1.0.0' }); + this.server.create('version-download', { version, date: '2020-01-13' }); + this.server.create('version-download', { version, date: '2020-01-14' }); + this.server.create('version-download', { version, date: '2020-01-15' }); + + let response = await fetch('/api/v1/crates/rand/1.0.0/downloads'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + version_downloads: [ + { + date: '2020-01-13', + downloads: 9380, + version: '1', + }, + { + date: '2020-01-14', + downloads: 16_415, + version: '1', + }, + { + date: '2020-01-15', + downloads: 23_450, + version: '1', + }, + ], + }); + }); +}); diff --git a/tests/mirage/crates/versions/list-test.js b/tests/mirage/crates/versions/list-test.js new file mode 100644 index 00000000000..00a5fe78c07 --- /dev/null +++ b/tests/mirage/crates/versions/list-test.js @@ -0,0 +1,99 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; + +import { setupTest } from '../../../helpers'; +import setupMirage from '../../../helpers/setup-mirage'; + +module('Mirage | GET /api/v1/crates/:id/versions', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('returns 404 for unknown crates', async function (assert) { + let response = await fetch('/api/v1/crates/foo/versions'); + assert.equal(response.status, 404); + assert.deepEqual(await response.json(), { errors: [{ detail: 'Not Found' }] }); + }); + + test('empty case', async function (assert) { + this.server.create('crate', { name: 'rand' }); + + let response = await fetch('/api/v1/crates/rand/versions'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + versions: [], + }); + }); + + test('returns all versions belonging to the specified crate', async function (assert) { + let user = this.server.create('user'); + let crate = this.server.create('crate', { name: 'rand' }); + this.server.create('version', { crate, num: '1.0.0' }); + this.server.create('version', { crate, num: '1.1.0', publishedBy: user }); + this.server.create('version', { crate, num: '1.2.0' }); + + let response = await fetch('/api/v1/crates/rand/versions'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + versions: [ + { + id: '1', + crate: 'rand', + crate_size: 0, + created_at: '2010-06-16T21:30:45Z', + dl_path: '/api/v1/crates/rand/1.0.0/download', + downloads: 0, + license: 'MIT/Apache-2.0', + links: { + dependencies: '/api/v1/crates/rand/1.0.0/dependencies', + version_downloads: '/api/v1/crates/rand/1.0.0/downloads', + }, + num: '1.0.0', + published_by: null, + updated_at: '2017-02-24T12:34:56Z', + yanked: false, + }, + { + id: '2', + crate: 'rand', + crate_size: 162_963, + created_at: '2010-06-16T21:30:45Z', + dl_path: '/api/v1/crates/rand/1.1.0/download', + downloads: 3702, + license: 'MIT', + links: { + dependencies: '/api/v1/crates/rand/1.1.0/dependencies', + version_downloads: '/api/v1/crates/rand/1.1.0/downloads', + }, + num: '1.1.0', + published_by: { + id: 1, + avatar: 'https://avatars1.githubusercontent.com/u/14631425?v=4', + login: 'user-1', + name: 'User 1', + url: 'https://github.com/user-1', + }, + updated_at: '2017-02-24T12:34:56Z', + yanked: false, + }, + { + id: '3', + crate: 'rand', + crate_size: 325_926, + created_at: '2010-06-16T21:30:45Z', + dl_path: '/api/v1/crates/rand/1.2.0/download', + downloads: 7404, + license: 'Apache-2.0', + links: { + dependencies: '/api/v1/crates/rand/1.2.0/dependencies', + version_downloads: '/api/v1/crates/rand/1.2.0/downloads', + }, + num: '1.2.0', + published_by: null, + updated_at: '2017-02-24T12:34:56Z', + yanked: false, + }, + ], + }); + }); +}); diff --git a/tests/mirage/invitations-test.js b/tests/mirage/invitations-test.js deleted file mode 100644 index 491a95991b4..00000000000 --- a/tests/mirage/invitations-test.js +++ /dev/null @@ -1,103 +0,0 @@ -import { module, test } from 'qunit'; - -import fetch from 'fetch'; - -import { setupTest } from 'cargo/tests/helpers'; - -import setupMirage from '../helpers/setup-mirage'; - -module('Mirage | Crate Owner Invitations', function (hooks) { - setupTest(hooks); - setupMirage(hooks); - - module('GET /api/v1/me/crate_owner_invitations', function () { - test('empty case', async function (assert) { - let user = this.server.create('user'); - this.server.create('mirage-session', { user }); - - let response = await fetch('/api/v1/me/crate_owner_invitations'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { crate_owner_invitations: [] }); - }); - - test('returns the list of invitations for the authenticated user', async function (assert) { - let nanomsg = this.server.create('crate', { name: 'nanomsg' }); - this.server.create('version', { crate: nanomsg }); - - let ember = this.server.create('crate', { name: 'ember-rs' }); - this.server.create('version', { crate: ember }); - - let user = this.server.create('user'); - this.server.create('mirage-session', { user }); - - let inviter = this.server.create('user', { name: 'janed' }); - this.server.create('crate-owner-invitation', { - crate: nanomsg, - createdAt: '2016-12-24T12:34:56Z', - invitee: user, - inviter, - }); - - let inviter2 = this.server.create('user', { name: 'wycats' }); - this.server.create('crate-owner-invitation', { - crate: ember, - createdAt: '2020-12-31T12:34:56Z', - invitee: user, - inviter: inviter2, - }); - - let response = await fetch('/api/v1/me/crate_owner_invitations'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - crate_owner_invitations: [ - { - crate_id: nanomsg.id, - crate_name: 'nanomsg', - created_at: '2016-12-24T12:34:56Z', - invited_by_username: 'janed', - invitee_id: Number(user.id), - inviter_id: Number(inviter.id), - }, - { - crate_id: ember.id, - crate_name: 'ember-rs', - created_at: '2020-12-31T12:34:56Z', - invited_by_username: 'wycats', - invitee_id: Number(user.id), - inviter_id: Number(inviter2.id), - }, - ], - users: [ - { - avatar: 'https://avatars1.githubusercontent.com/u/14631425?v=4', - id: Number(inviter.id), - login: 'janed', - name: 'janed', - url: 'https://github.com/janed', - }, - { - avatar: 'https://avatars1.githubusercontent.com/u/14631425?v=4', - id: Number(inviter2.id), - login: 'wycats', - name: 'wycats', - url: 'https://github.com/wycats', - }, - ], - }); - }); - - test('returns an error if unauthenticated', async function (assert) { - let response = await fetch('/api/v1/me/crate_owner_invitations'); - assert.equal(response.status, 403); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - errors: [{ detail: 'must be logged in to perform that action' }], - }); - }); - }); -}); diff --git a/tests/mirage/keywords-test.js b/tests/mirage/keywords-test.js deleted file mode 100644 index 3bd23e30a73..00000000000 --- a/tests/mirage/keywords-test.js +++ /dev/null @@ -1,132 +0,0 @@ -import { module, test } from 'qunit'; - -import fetch from 'fetch'; - -import { setupTest } from 'cargo/tests/helpers'; - -import setupMirage from '../helpers/setup-mirage'; - -module('Mirage | Keywords', function (hooks) { - setupTest(hooks); - setupMirage(hooks); - - module('GET /api/v1/keywords', function () { - test('empty case', async function (assert) { - let response = await fetch('/api/v1/keywords'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - keywords: [], - meta: { - total: 0, - }, - }); - }); - - test('returns a paginated keywords list', async function (assert) { - this.server.create('keyword', { keyword: 'api' }); - this.server.createList('keyword', 2); - - let response = await fetch('/api/v1/keywords'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - keywords: [ - { - id: 'api', - crates_cnt: 0, - keyword: 'api', - }, - { - id: 'keyword-2', - crates_cnt: 0, - keyword: 'keyword-2', - }, - { - id: 'keyword-3', - crates_cnt: 0, - keyword: 'keyword-3', - }, - ], - meta: { - total: 3, - }, - }); - }); - - test('never returns more than 10 results', async function (assert) { - this.server.createList('keyword', 25); - - let response = await fetch('/api/v1/keywords'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.equal(responsePayload.keywords.length, 10); - assert.equal(responsePayload.meta.total, 25); - }); - - test('supports `page` and `per_page` parameters', async function (assert) { - this.server.createList('keyword', 25, { - keyword: i => `k${String(i + 1).padStart(2, '0')}`, - }); - - let response = await fetch('/api/v1/keywords?page=2&per_page=5'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.equal(responsePayload.keywords.length, 5); - assert.deepEqual( - responsePayload.keywords.map(it => it.id), - ['k06', 'k07', 'k08', 'k09', 'k10'], - ); - assert.equal(responsePayload.meta.total, 25); - }); - }); - - module('GET /api/v1/keywords/:id', function () { - test('returns 404 for unknown keywords', async function (assert) { - let response = await fetch('/api/v1/keywords/foo'); - assert.equal(response.status, 404); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { errors: [{ detail: 'Not Found' }] }); - }); - - test('returns a keyword object for known keywords', async function (assert) { - this.server.create('keyword', { keyword: 'cli' }); - - let response = await fetch('/api/v1/keywords/cli'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - keyword: { - id: 'cli', - crates_cnt: 0, - keyword: 'cli', - }, - }); - }); - - test('calculates `crates_cnt` correctly', async function (assert) { - this.server.create('keyword', { keyword: 'cli' }); - this.server.createList('crate', 7, { keywordIds: ['cli'] }); - this.server.create('keyword', { keyword: 'not-cli' }); - this.server.createList('crate', 3, { keywordIds: ['not-cli'] }); - - let response = await fetch('/api/v1/keywords/cli'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - keyword: { - id: 'cli', - crates_cnt: 7, - keyword: 'cli', - }, - }); - }); - }); -}); diff --git a/tests/mirage/keywords/get-by-id-test.js b/tests/mirage/keywords/get-by-id-test.js new file mode 100644 index 00000000000..4971499f6a8 --- /dev/null +++ b/tests/mirage/keywords/get-by-id-test.js @@ -0,0 +1,48 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; + +import { setupTest } from '../../helpers'; +import setupMirage from '../../helpers/setup-mirage'; + +module('Mirage | GET /api/v1/keywords/:id', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('returns 404 for unknown keywords', async function (assert) { + let response = await fetch('/api/v1/keywords/foo'); + assert.equal(response.status, 404); + assert.deepEqual(await response.json(), { errors: [{ detail: 'Not Found' }] }); + }); + + test('returns a keyword object for known keywords', async function (assert) { + this.server.create('keyword', { keyword: 'cli' }); + + let response = await fetch('/api/v1/keywords/cli'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + keyword: { + id: 'cli', + crates_cnt: 0, + keyword: 'cli', + }, + }); + }); + + test('calculates `crates_cnt` correctly', async function (assert) { + this.server.create('keyword', { keyword: 'cli' }); + this.server.createList('crate', 7, { keywordIds: ['cli'] }); + this.server.create('keyword', { keyword: 'not-cli' }); + this.server.createList('crate', 3, { keywordIds: ['not-cli'] }); + + let response = await fetch('/api/v1/keywords/cli'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + keyword: { + id: 'cli', + crates_cnt: 7, + keyword: 'cli', + }, + }); + }); +}); diff --git a/tests/mirage/keywords/list-test.js b/tests/mirage/keywords/list-test.js new file mode 100644 index 00000000000..b4ac4905074 --- /dev/null +++ b/tests/mirage/keywords/list-test.js @@ -0,0 +1,80 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; + +import { setupTest } from '../../helpers'; +import setupMirage from '../../helpers/setup-mirage'; + +module('Mirage | GET /api/v1/keywords', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('empty case', async function (assert) { + let response = await fetch('/api/v1/keywords'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + keywords: [], + meta: { + total: 0, + }, + }); + }); + + test('returns a paginated keywords list', async function (assert) { + this.server.create('keyword', { keyword: 'api' }); + this.server.createList('keyword', 2); + + let response = await fetch('/api/v1/keywords'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + keywords: [ + { + id: 'api', + crates_cnt: 0, + keyword: 'api', + }, + { + id: 'keyword-2', + crates_cnt: 0, + keyword: 'keyword-2', + }, + { + id: 'keyword-3', + crates_cnt: 0, + keyword: 'keyword-3', + }, + ], + meta: { + total: 3, + }, + }); + }); + + test('never returns more than 10 results', async function (assert) { + this.server.createList('keyword', 25); + + let response = await fetch('/api/v1/keywords'); + assert.equal(response.status, 200); + + let responsePayload = await response.json(); + assert.equal(responsePayload.keywords.length, 10); + assert.equal(responsePayload.meta.total, 25); + }); + + test('supports `page` and `per_page` parameters', async function (assert) { + this.server.createList('keyword', 25, { + keyword: i => `k${String(i + 1).padStart(2, '0')}`, + }); + + let response = await fetch('/api/v1/keywords?page=2&per_page=5'); + assert.equal(response.status, 200); + + let responsePayload = await response.json(); + assert.equal(responsePayload.keywords.length, 5); + assert.deepEqual( + responsePayload.keywords.map(it => it.id), + ['k06', 'k07', 'k08', 'k09', 'k10'], + ); + assert.equal(responsePayload.meta.total, 25); + }); +}); diff --git a/tests/mirage/me-test.js b/tests/mirage/me-test.js deleted file mode 100644 index 4ddb00bf94f..00000000000 --- a/tests/mirage/me-test.js +++ /dev/null @@ -1,325 +0,0 @@ -import { module, test } from 'qunit'; - -import fetch from 'fetch'; -import timekeeper from 'timekeeper'; - -import { setupTest } from 'cargo/tests/helpers'; - -import setupMirage from '../helpers/setup-mirage'; - -module('Mirage | /me', function (hooks) { - setupTest(hooks); - setupMirage(hooks); - - module('GET /api/v1/me', function () { - test('returns the `user` resource including the private fields', async function (assert) { - let user = this.server.create('user'); - this.server.create('mirage-session', { user }); - - let response = await fetch('/api/v1/me'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - user: { - id: 1, - avatar: 'https://avatars1.githubusercontent.com/u/14631425?v=4', - email: 'user-1@crates.io', - email_verification_sent: true, - email_verified: true, - login: 'user-1', - name: 'User 1', - url: 'https://github.com/user-1', - }, - owned_crates: [], - }); - }); - - test('returns a list of `owned_crates`', async function (assert) { - let user = this.server.create('user'); - this.server.create('mirage-session', { user }); - - let [crate1, , crate3] = this.server.createList('crate', 3); - - this.server.create('crate-ownership', { crate: crate1, user }); - this.server.create('crate-ownership', { crate: crate3, user }); - - let response = await fetch('/api/v1/me'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload.owned_crates, [ - { id: crate1.id, name: 'crate-0', email_notifications: true }, - { id: crate3.id, name: 'crate-2', email_notifications: true }, - ]); - }); - - test('returns an error if unauthenticated', async function (assert) { - this.server.create('user'); - - let response = await fetch('/api/v1/me'); - assert.equal(response.status, 403); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - errors: [{ detail: 'must be logged in to perform that action' }], - }); - }); - }); - - module('GET /api/v1/me/tokens', function () { - test('returns the list of API token for the authenticated `user`', async function (assert) { - let user = this.server.create('user'); - this.server.create('mirage-session', { user }); - - this.server.create('api-token', { user, createdAt: '2017-11-19T12:59:22Z' }); - this.server.create('api-token', { user, createdAt: '2017-11-19T13:59:22Z' }); - this.server.create('api-token', { user, createdAt: '2017-11-19T14:59:22Z' }); - - let response = await fetch('/api/v1/me/tokens'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - api_tokens: [ - { - id: 3, - created_at: '2017-11-19T14:59:22.000Z', - last_used_at: null, - name: 'API Token 3', - }, - { - id: 2, - created_at: '2017-11-19T13:59:22.000Z', - last_used_at: null, - name: 'API Token 2', - }, - { - id: 1, - created_at: '2017-11-19T12:59:22.000Z', - last_used_at: null, - name: 'API Token 1', - }, - ], - }); - }); - - test('empty list case', async function (assert) { - let user = this.server.create('user'); - this.server.create('mirage-session', { user }); - - let response = await fetch('/api/v1/me/tokens'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { api_tokens: [] }); - }); - - test('returns an error if unauthenticated', async function (assert) { - let response = await fetch('/api/v1/me/tokens'); - assert.equal(response.status, 403); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - errors: [{ detail: 'must be logged in to perform that action' }], - }); - }); - }); - - module('PUT /api/v1/me/tokens', function () { - test('creates a new API token', async function (assert) { - timekeeper.freeze(new Date('2017-11-20T11:23:45Z')); - - let user = this.server.create('user'); - this.server.create('mirage-session', { user }); - - let body = JSON.stringify({ api_token: { name: 'foooo' } }); - let response = await fetch('/api/v1/me/tokens', { method: 'PUT', body }); - assert.equal(response.status, 200); - - let token = this.server.schema.apiTokens.all().models[0]; - assert.ok(token); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - api_token: { - id: 1, - created_at: '2017-11-20T11:23:45.000Z', - last_used_at: null, - name: 'foooo', - revoked: false, - token: token.token, - }, - }); - }); - - test('returns an error if unauthenticated', async function (assert) { - let body = JSON.stringify({ api_token: {} }); - let response = await fetch('/api/v1/me/tokens', { method: 'PUT', body }); - assert.equal(response.status, 403); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - errors: [{ detail: 'must be logged in to perform that action' }], - }); - }); - }); - - module('DELETE /api/v1/me/tokens/:tokenId', function () { - test('revokes an API token', async function (assert) { - let user = this.server.create('user'); - this.server.create('mirage-session', { user }); - - let token = this.server.create('api-token', { user }); - - let response = await fetch(`/api/v1/me/tokens/${token.id}`, { method: 'DELETE' }); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, {}); - - let tokens = this.server.schema.apiTokens.all().models; - assert.equal(tokens.length, 0); - }); - - test('returns an error if unauthenticated', async function (assert) { - let user = this.server.create('user'); - let token = this.server.create('api-token', { user }); - - let response = await fetch(`/api/v1/me/tokens/${token.id}`, { method: 'DELETE' }); - assert.equal(response.status, 403); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - errors: [{ detail: 'must be logged in to perform that action' }], - }); - }); - }); - - module('GET /api/v1/me/updates', function () { - test('returns 403 for unauthenticated user', async function (assert) { - let response = await fetch('/api/v1/me/updates'); - assert.equal(response.status, 403); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - errors: [{ detail: 'must be logged in to perform that action' }], - }); - }); - - test('returns latest versions of followed crates', async function (assert) { - let foo = this.server.create('crate', { name: 'foo' }); - this.server.create('version', { crate: foo, num: '1.2.3' }); - - let bar = this.server.create('crate', { name: 'bar' }); - this.server.create('version', { crate: bar, num: '0.8.6' }); - - let user = this.server.create('user', { followedCrates: [foo] }); - this.authenticateAs(user); - - let response = await fetch('/api/v1/me/updates'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - versions: [ - { - id: '1', - crate: 'foo', - crate_size: 0, - created_at: '2010-06-16T21:30:45Z', - dl_path: '/api/v1/crates/foo/1.2.3/download', - downloads: 0, - license: 'MIT/Apache-2.0', - links: { - dependencies: '/api/v1/crates/foo/1.2.3/dependencies', - version_downloads: '/api/v1/crates/foo/1.2.3/downloads', - }, - num: '1.2.3', - published_by: null, - updated_at: '2017-02-24T12:34:56Z', - yanked: false, - }, - ], - meta: { - more: false, - }, - }); - }); - - test('empty case', async function (assert) { - let user = this.server.create('user'); - this.authenticateAs(user); - - let response = await fetch('/api/v1/me/updates'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - versions: [], - meta: { more: false }, - }); - }); - - test('supports pagination', async function (assert) { - let crate = this.server.create('crate', { name: 'foo' }); - this.server.createList('version', 25, { crate }); - - let user = this.server.create('user', { followedCrates: [crate] }); - this.authenticateAs(user); - - let response = await fetch('/api/v1/me/updates?page=2'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.equal(responsePayload.versions.length, 10); - assert.deepEqual( - responsePayload.versions.map(it => it.id), - ['15', '14', '13', '12', '11', '10', '9', '8', '7', '6'], - ); - assert.deepEqual(responsePayload.meta, { more: true }); - }); - }); - - module('GET /api/v1/confirm/:token', function () { - test('returns `ok: true` for a known token (unauthenticated)', async function (assert) { - let user = this.server.create('user', { emailVerificationToken: 'foo' }); - assert.strictEqual(user.emailVerified, false); - - let response = await fetch('/api/v1/confirm/foo', { method: 'PUT' }); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { ok: true }); - - user.reload(); - assert.strictEqual(user.emailVerified, true); - }); - - test('returns `ok: true` for a known token (authenticated)', async function (assert) { - let user = this.server.create('user', { emailVerificationToken: 'foo' }); - assert.strictEqual(user.emailVerified, false); - - this.server.create('mirage-session', { user }); - - let response = await fetch('/api/v1/confirm/foo', { method: 'PUT' }); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { ok: true }); - - user.reload(); - assert.strictEqual(user.emailVerified, true); - }); - - test('returns an error for unknown tokens', async function (assert) { - let response = await fetch('/api/v1/confirm/unknown', { method: 'PUT' }); - assert.equal(response.status, 400); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - errors: [{ detail: 'Email belonging to token not found.' }], - }); - }); - }); -}); diff --git a/tests/mirage/me/crate-owner-invitations/list-test.js b/tests/mirage/me/crate-owner-invitations/list-test.js new file mode 100644 index 00000000000..82a1e0b10da --- /dev/null +++ b/tests/mirage/me/crate-owner-invitations/list-test.js @@ -0,0 +1,94 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; + +import { setupTest } from '../../../helpers'; +import setupMirage from '../../../helpers/setup-mirage'; + +module('Mirage | GET /api/v1/me/crate_owner_invitations', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('empty case', async function (assert) { + let user = this.server.create('user'); + this.server.create('mirage-session', { user }); + + let response = await fetch('/api/v1/me/crate_owner_invitations'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { crate_owner_invitations: [] }); + }); + + test('returns the list of invitations for the authenticated user', async function (assert) { + let nanomsg = this.server.create('crate', { name: 'nanomsg' }); + this.server.create('version', { crate: nanomsg }); + + let ember = this.server.create('crate', { name: 'ember-rs' }); + this.server.create('version', { crate: ember }); + + let user = this.server.create('user'); + this.server.create('mirage-session', { user }); + + let inviter = this.server.create('user', { name: 'janed' }); + this.server.create('crate-owner-invitation', { + crate: nanomsg, + createdAt: '2016-12-24T12:34:56Z', + invitee: user, + inviter, + }); + + let inviter2 = this.server.create('user', { name: 'wycats' }); + this.server.create('crate-owner-invitation', { + crate: ember, + createdAt: '2020-12-31T12:34:56Z', + invitee: user, + inviter: inviter2, + }); + + let response = await fetch('/api/v1/me/crate_owner_invitations'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + crate_owner_invitations: [ + { + crate_id: nanomsg.id, + crate_name: 'nanomsg', + created_at: '2016-12-24T12:34:56Z', + invited_by_username: 'janed', + invitee_id: Number(user.id), + inviter_id: Number(inviter.id), + }, + { + crate_id: ember.id, + crate_name: 'ember-rs', + created_at: '2020-12-31T12:34:56Z', + invited_by_username: 'wycats', + invitee_id: Number(user.id), + inviter_id: Number(inviter2.id), + }, + ], + users: [ + { + avatar: 'https://avatars1.githubusercontent.com/u/14631425?v=4', + id: Number(inviter.id), + login: 'janed', + name: 'janed', + url: 'https://github.com/janed', + }, + { + avatar: 'https://avatars1.githubusercontent.com/u/14631425?v=4', + id: Number(inviter2.id), + login: 'wycats', + name: 'wycats', + url: 'https://github.com/wycats', + }, + ], + }); + }); + + test('returns an error if unauthenticated', async function (assert) { + let response = await fetch('/api/v1/me/crate_owner_invitations'); + assert.equal(response.status, 403); + assert.deepEqual(await response.json(), { + errors: [{ detail: 'must be logged in to perform that action' }], + }); + }); +}); diff --git a/tests/mirage/me/get-test.js b/tests/mirage/me/get-test.js new file mode 100644 index 00000000000..3188f013864 --- /dev/null +++ b/tests/mirage/me/get-test.js @@ -0,0 +1,61 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; + +import { setupTest } from '../../helpers'; +import setupMirage from '../../helpers/setup-mirage'; + +module('Mirage | GET /api/v1/me', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('returns the `user` resource including the private fields', async function (assert) { + let user = this.server.create('user'); + this.server.create('mirage-session', { user }); + + let response = await fetch('/api/v1/me'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + user: { + id: 1, + avatar: 'https://avatars1.githubusercontent.com/u/14631425?v=4', + email: 'user-1@crates.io', + email_verification_sent: true, + email_verified: true, + login: 'user-1', + name: 'User 1', + url: 'https://github.com/user-1', + }, + owned_crates: [], + }); + }); + + test('returns a list of `owned_crates`', async function (assert) { + let user = this.server.create('user'); + this.server.create('mirage-session', { user }); + + let [crate1, , crate3] = this.server.createList('crate', 3); + + this.server.create('crate-ownership', { crate: crate1, user }); + this.server.create('crate-ownership', { crate: crate3, user }); + + let response = await fetch('/api/v1/me'); + assert.equal(response.status, 200); + + let responsePayload = await response.json(); + assert.deepEqual(responsePayload.owned_crates, [ + { id: crate1.id, name: 'crate-0', email_notifications: true }, + { id: crate3.id, name: 'crate-2', email_notifications: true }, + ]); + }); + + test('returns an error if unauthenticated', async function (assert) { + this.server.create('user'); + + let response = await fetch('/api/v1/me'); + assert.equal(response.status, 403); + assert.deepEqual(await response.json(), { + errors: [{ detail: 'must be logged in to perform that action' }], + }); + }); +}); diff --git a/tests/mirage/me/tokens/create-test.js b/tests/mirage/me/tokens/create-test.js new file mode 100644 index 00000000000..0877f5a87e1 --- /dev/null +++ b/tests/mirage/me/tokens/create-test.js @@ -0,0 +1,46 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; +import timekeeper from 'timekeeper'; + +import { setupTest } from '../../../helpers'; +import setupMirage from '../../../helpers/setup-mirage'; + +module('Mirage | PUT /api/v1/me/tokens', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('creates a new API token', async function (assert) { + timekeeper.freeze(new Date('2017-11-20T11:23:45Z')); + + let user = this.server.create('user'); + this.server.create('mirage-session', { user }); + + let body = JSON.stringify({ api_token: { name: 'foooo' } }); + let response = await fetch('/api/v1/me/tokens', { method: 'PUT', body }); + assert.equal(response.status, 200); + + let token = this.server.schema.apiTokens.all().models[0]; + assert.ok(token); + + assert.deepEqual(await response.json(), { + api_token: { + id: 1, + created_at: '2017-11-20T11:23:45.000Z', + last_used_at: null, + name: 'foooo', + revoked: false, + token: token.token, + }, + }); + }); + + test('returns an error if unauthenticated', async function (assert) { + let body = JSON.stringify({ api_token: {} }); + let response = await fetch('/api/v1/me/tokens', { method: 'PUT', body }); + assert.equal(response.status, 403); + assert.deepEqual(await response.json(), { + errors: [{ detail: 'must be logged in to perform that action' }], + }); + }); +}); diff --git a/tests/mirage/me/tokens/delete-by-id-test.js b/tests/mirage/me/tokens/delete-by-id-test.js new file mode 100644 index 00000000000..1231bd94b01 --- /dev/null +++ b/tests/mirage/me/tokens/delete-by-id-test.js @@ -0,0 +1,36 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; + +import { setupTest } from '../../../helpers'; +import setupMirage from '../../../helpers/setup-mirage'; + +module('Mirage | DELETE /api/v1/me/tokens/:tokenId', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('revokes an API token', async function (assert) { + let user = this.server.create('user'); + this.server.create('mirage-session', { user }); + + let token = this.server.create('api-token', { user }); + + let response = await fetch(`/api/v1/me/tokens/${token.id}`, { method: 'DELETE' }); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), {}); + + let tokens = this.server.schema.apiTokens.all().models; + assert.equal(tokens.length, 0); + }); + + test('returns an error if unauthenticated', async function (assert) { + let user = this.server.create('user'); + let token = this.server.create('api-token', { user }); + + let response = await fetch(`/api/v1/me/tokens/${token.id}`, { method: 'DELETE' }); + assert.equal(response.status, 403); + assert.deepEqual(await response.json(), { + errors: [{ detail: 'must be logged in to perform that action' }], + }); + }); +}); diff --git a/tests/mirage/me/tokens/list-test.js b/tests/mirage/me/tokens/list-test.js new file mode 100644 index 00000000000..4355cea07d6 --- /dev/null +++ b/tests/mirage/me/tokens/list-test.js @@ -0,0 +1,62 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; + +import { setupTest } from '../../../helpers'; +import setupMirage from '../../../helpers/setup-mirage'; + +module('Mirage | GET /api/v1/me/tokens', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('returns the list of API token for the authenticated `user`', async function (assert) { + let user = this.server.create('user'); + this.server.create('mirage-session', { user }); + + this.server.create('api-token', { user, createdAt: '2017-11-19T12:59:22Z' }); + this.server.create('api-token', { user, createdAt: '2017-11-19T13:59:22Z' }); + this.server.create('api-token', { user, createdAt: '2017-11-19T14:59:22Z' }); + + let response = await fetch('/api/v1/me/tokens'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + api_tokens: [ + { + id: 3, + created_at: '2017-11-19T14:59:22.000Z', + last_used_at: null, + name: 'API Token 3', + }, + { + id: 2, + created_at: '2017-11-19T13:59:22.000Z', + last_used_at: null, + name: 'API Token 2', + }, + { + id: 1, + created_at: '2017-11-19T12:59:22.000Z', + last_used_at: null, + name: 'API Token 1', + }, + ], + }); + }); + + test('empty list case', async function (assert) { + let user = this.server.create('user'); + this.server.create('mirage-session', { user }); + + let response = await fetch('/api/v1/me/tokens'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { api_tokens: [] }); + }); + + test('returns an error if unauthenticated', async function (assert) { + let response = await fetch('/api/v1/me/tokens'); + assert.equal(response.status, 403); + assert.deepEqual(await response.json(), { + errors: [{ detail: 'must be logged in to perform that action' }], + }); + }); +}); diff --git a/tests/mirage/me/updates/list-test.js b/tests/mirage/me/updates/list-test.js new file mode 100644 index 00000000000..59378be63a9 --- /dev/null +++ b/tests/mirage/me/updates/list-test.js @@ -0,0 +1,88 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; + +import { setupTest } from '../../../helpers'; +import setupMirage from '../../../helpers/setup-mirage'; + +module('Mirage | GET /api/v1/me/updates', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('returns 403 for unauthenticated user', async function (assert) { + let response = await fetch('/api/v1/me/updates'); + assert.equal(response.status, 403); + assert.deepEqual(await response.json(), { + errors: [{ detail: 'must be logged in to perform that action' }], + }); + }); + + test('returns latest versions of followed crates', async function (assert) { + let foo = this.server.create('crate', { name: 'foo' }); + this.server.create('version', { crate: foo, num: '1.2.3' }); + + let bar = this.server.create('crate', { name: 'bar' }); + this.server.create('version', { crate: bar, num: '0.8.6' }); + + let user = this.server.create('user', { followedCrates: [foo] }); + this.authenticateAs(user); + + let response = await fetch('/api/v1/me/updates'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + versions: [ + { + id: '1', + crate: 'foo', + crate_size: 0, + created_at: '2010-06-16T21:30:45Z', + dl_path: '/api/v1/crates/foo/1.2.3/download', + downloads: 0, + license: 'MIT/Apache-2.0', + links: { + dependencies: '/api/v1/crates/foo/1.2.3/dependencies', + version_downloads: '/api/v1/crates/foo/1.2.3/downloads', + }, + num: '1.2.3', + published_by: null, + updated_at: '2017-02-24T12:34:56Z', + yanked: false, + }, + ], + meta: { + more: false, + }, + }); + }); + + test('empty case', async function (assert) { + let user = this.server.create('user'); + this.authenticateAs(user); + + let response = await fetch('/api/v1/me/updates'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + versions: [], + meta: { more: false }, + }); + }); + + test('supports pagination', async function (assert) { + let crate = this.server.create('crate', { name: 'foo' }); + this.server.createList('version', 25, { crate }); + + let user = this.server.create('user', { followedCrates: [crate] }); + this.authenticateAs(user); + + let response = await fetch('/api/v1/me/updates?page=2'); + assert.equal(response.status, 200); + + let responsePayload = await response.json(); + assert.equal(responsePayload.versions.length, 10); + assert.deepEqual( + responsePayload.versions.map(it => it.id), + ['15', '14', '13', '12', '11', '10', '9', '8', '7', '6'], + ); + assert.deepEqual(responsePayload.meta, { more: true }); + }); +}); diff --git a/tests/mirage/private/session/delete-test.js b/tests/mirage/private/session/delete-test.js new file mode 100644 index 00000000000..5b31df9f241 --- /dev/null +++ b/tests/mirage/private/session/delete-test.js @@ -0,0 +1,30 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; + +import { setupTest } from '../../../helpers'; +import setupMirage from '../../../helpers/setup-mirage'; + +module('Mirage | DELETE /api/private/session', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('returns 200 when authenticated', async function (assert) { + let user = this.server.create('user'); + this.server.create('mirage-session', { user }); + + let response = await fetch('/api/private/session', { method: 'DELETE' }); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { ok: true }); + + assert.notOk(this.server.schema.mirageSessions.first()); + }); + + test('returns 200 when unauthenticated', async function (assert) { + let response = await fetch('/api/private/session', { method: 'DELETE' }); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { ok: true }); + + assert.notOk(this.server.schema.mirageSessions.first()); + }); +}); diff --git a/tests/mirage/session-test.js b/tests/mirage/session-test.js deleted file mode 100644 index af007351027..00000000000 --- a/tests/mirage/session-test.js +++ /dev/null @@ -1,37 +0,0 @@ -import { module, test } from 'qunit'; - -import fetch from 'fetch'; - -import { setupTest } from 'cargo/tests/helpers'; - -import setupMirage from '../helpers/setup-mirage'; - -module('Mirage | Session', function (hooks) { - setupTest(hooks); - setupMirage(hooks); - - module('DELETE /api/private/session', function () { - test('returns 200 when authenticated', async function (assert) { - let user = this.server.create('user'); - this.server.create('mirage-session', { user }); - - let response = await fetch('/api/private/session', { method: 'DELETE' }); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { ok: true }); - - assert.notOk(this.server.schema.mirageSessions.first()); - }); - - test('returns 200 when unauthenticated', async function (assert) { - let response = await fetch('/api/private/session', { method: 'DELETE' }); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { ok: true }); - - assert.notOk(this.server.schema.mirageSessions.first()); - }); - }); -}); diff --git a/tests/mirage/summary-test.js b/tests/mirage/summary-test.js index 6546b144ad1..5c5ad0e944d 100644 --- a/tests/mirage/summary-test.js +++ b/tests/mirage/summary-test.js @@ -6,166 +6,162 @@ import { setupTest } from 'cargo/tests/helpers'; import setupMirage from '../helpers/setup-mirage'; -module('Mirage | Summary', function (hooks) { +module('Mirage | GET /api/v1/summary', function (hooks) { setupTest(hooks); setupMirage(hooks); - module('GET /api/v1/summary', function () { - test('empty case', async function (assert) { - let response = await fetch('/api/v1/summary'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - just_updated: [], - most_downloaded: [], - most_recently_downloaded: [], - new_crates: [], - num_crates: 0, - num_downloads: 0, - popular_categories: [], - popular_keywords: [], - }); + test('empty case', async function (assert) { + let response = await fetch('/api/v1/summary'); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + just_updated: [], + most_downloaded: [], + most_recently_downloaded: [], + new_crates: [], + num_crates: 0, + num_downloads: 0, + popular_categories: [], + popular_keywords: [], }); + }); - test('returns the data for the front page', async function (assert) { - this.server.createList('category', 15); - this.server.createList('keyword', 25); - let crates = this.server.createList('crate', 20); - this.server.createList('version', crates.length, { crate: i => crates[i] }); - - let response = await fetch('/api/v1/summary'); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - - assert.equal(responsePayload.just_updated.length, 10); - assert.deepEqual(responsePayload.just_updated[0], { - id: 'crate-0', - badges: [], - categories: [], - created_at: '2010-06-16T21:30:45Z', - description: 'This is the description for the crate called "crate-0"', - documentation: null, - downloads: 0, - homepage: null, - keywords: [], - links: { - owner_team: '/api/v1/crates/crate-0/owner_team', - owner_user: '/api/v1/crates/crate-0/owner_user', - reverse_dependencies: '/api/v1/crates/crate-0/reverse_dependencies', - version_downloads: '/api/v1/crates/crate-0/downloads', - versions: '/api/v1/crates/crate-0/versions', - }, - max_version: '1.0.0', - max_stable_version: '1.0.0', - name: 'crate-0', - newest_version: '1.0.0', - repository: null, - updated_at: '2017-02-24T12:34:56Z', - versions: null, - }); - - assert.equal(responsePayload.most_downloaded.length, 10); - assert.deepEqual(responsePayload.most_downloaded[0], { - id: 'crate-4', - badges: [], - categories: [], - created_at: '2010-06-16T21:30:45Z', - description: 'This is the description for the crate called "crate-4"', - documentation: null, - downloads: 148_140, - homepage: null, - keywords: [], - links: { - owner_team: '/api/v1/crates/crate-4/owner_team', - owner_user: '/api/v1/crates/crate-4/owner_user', - reverse_dependencies: '/api/v1/crates/crate-4/reverse_dependencies', - version_downloads: '/api/v1/crates/crate-4/downloads', - versions: '/api/v1/crates/crate-4/versions', - }, - max_version: '1.0.4', - max_stable_version: '1.0.4', - name: 'crate-4', - newest_version: '1.0.4', - repository: null, - updated_at: '2017-02-24T12:34:56Z', - versions: null, - }); + test('returns the data for the front page', async function (assert) { + this.server.createList('category', 15); + this.server.createList('keyword', 25); + let crates = this.server.createList('crate', 20); + this.server.createList('version', crates.length, { crate: i => crates[i] }); + + let response = await fetch('/api/v1/summary'); + assert.equal(response.status, 200); + + let responsePayload = await response.json(); + + assert.equal(responsePayload.just_updated.length, 10); + assert.deepEqual(responsePayload.just_updated[0], { + id: 'crate-0', + badges: [], + categories: [], + created_at: '2010-06-16T21:30:45Z', + description: 'This is the description for the crate called "crate-0"', + documentation: null, + downloads: 0, + homepage: null, + keywords: [], + links: { + owner_team: '/api/v1/crates/crate-0/owner_team', + owner_user: '/api/v1/crates/crate-0/owner_user', + reverse_dependencies: '/api/v1/crates/crate-0/reverse_dependencies', + version_downloads: '/api/v1/crates/crate-0/downloads', + versions: '/api/v1/crates/crate-0/versions', + }, + max_version: '1.0.0', + max_stable_version: '1.0.0', + name: 'crate-0', + newest_version: '1.0.0', + repository: null, + updated_at: '2017-02-24T12:34:56Z', + versions: null, + }); - assert.equal(responsePayload.most_recently_downloaded.length, 10); - assert.deepEqual(responsePayload.most_recently_downloaded[0], { - id: 'crate-0', - badges: [], - categories: [], - created_at: '2010-06-16T21:30:45Z', - description: 'This is the description for the crate called "crate-0"', - documentation: null, - downloads: 0, - homepage: null, - keywords: [], - links: { - owner_team: '/api/v1/crates/crate-0/owner_team', - owner_user: '/api/v1/crates/crate-0/owner_user', - reverse_dependencies: '/api/v1/crates/crate-0/reverse_dependencies', - version_downloads: '/api/v1/crates/crate-0/downloads', - versions: '/api/v1/crates/crate-0/versions', - }, - max_version: '1.0.0', - max_stable_version: '1.0.0', - name: 'crate-0', - newest_version: '1.0.0', - repository: null, - updated_at: '2017-02-24T12:34:56Z', - versions: null, - }); + assert.equal(responsePayload.most_downloaded.length, 10); + assert.deepEqual(responsePayload.most_downloaded[0], { + id: 'crate-4', + badges: [], + categories: [], + created_at: '2010-06-16T21:30:45Z', + description: 'This is the description for the crate called "crate-4"', + documentation: null, + downloads: 148_140, + homepage: null, + keywords: [], + links: { + owner_team: '/api/v1/crates/crate-4/owner_team', + owner_user: '/api/v1/crates/crate-4/owner_user', + reverse_dependencies: '/api/v1/crates/crate-4/reverse_dependencies', + version_downloads: '/api/v1/crates/crate-4/downloads', + versions: '/api/v1/crates/crate-4/versions', + }, + max_version: '1.0.4', + max_stable_version: '1.0.4', + name: 'crate-4', + newest_version: '1.0.4', + repository: null, + updated_at: '2017-02-24T12:34:56Z', + versions: null, + }); - assert.equal(responsePayload.new_crates.length, 10); - assert.deepEqual(responsePayload.new_crates[0], { - id: 'crate-0', - badges: [], - categories: [], - created_at: '2010-06-16T21:30:45Z', - description: 'This is the description for the crate called "crate-0"', - documentation: null, - downloads: 0, - homepage: null, - keywords: [], - links: { - owner_team: '/api/v1/crates/crate-0/owner_team', - owner_user: '/api/v1/crates/crate-0/owner_user', - reverse_dependencies: '/api/v1/crates/crate-0/reverse_dependencies', - version_downloads: '/api/v1/crates/crate-0/downloads', - versions: '/api/v1/crates/crate-0/versions', - }, - max_version: '1.0.0', - max_stable_version: '1.0.0', - name: 'crate-0', - newest_version: '1.0.0', - repository: null, - updated_at: '2017-02-24T12:34:56Z', - versions: null, - }); + assert.equal(responsePayload.most_recently_downloaded.length, 10); + assert.deepEqual(responsePayload.most_recently_downloaded[0], { + id: 'crate-0', + badges: [], + categories: [], + created_at: '2010-06-16T21:30:45Z', + description: 'This is the description for the crate called "crate-0"', + documentation: null, + downloads: 0, + homepage: null, + keywords: [], + links: { + owner_team: '/api/v1/crates/crate-0/owner_team', + owner_user: '/api/v1/crates/crate-0/owner_user', + reverse_dependencies: '/api/v1/crates/crate-0/reverse_dependencies', + version_downloads: '/api/v1/crates/crate-0/downloads', + versions: '/api/v1/crates/crate-0/versions', + }, + max_version: '1.0.0', + max_stable_version: '1.0.0', + name: 'crate-0', + newest_version: '1.0.0', + repository: null, + updated_at: '2017-02-24T12:34:56Z', + versions: null, + }); - assert.equal(responsePayload.num_crates, 20); - assert.equal(responsePayload.num_downloads, 1_419_675); + assert.equal(responsePayload.new_crates.length, 10); + assert.deepEqual(responsePayload.new_crates[0], { + id: 'crate-0', + badges: [], + categories: [], + created_at: '2010-06-16T21:30:45Z', + description: 'This is the description for the crate called "crate-0"', + documentation: null, + downloads: 0, + homepage: null, + keywords: [], + links: { + owner_team: '/api/v1/crates/crate-0/owner_team', + owner_user: '/api/v1/crates/crate-0/owner_user', + reverse_dependencies: '/api/v1/crates/crate-0/reverse_dependencies', + version_downloads: '/api/v1/crates/crate-0/downloads', + versions: '/api/v1/crates/crate-0/versions', + }, + max_version: '1.0.0', + max_stable_version: '1.0.0', + name: 'crate-0', + newest_version: '1.0.0', + repository: null, + updated_at: '2017-02-24T12:34:56Z', + versions: null, + }); - assert.equal(responsePayload.popular_categories.length, 10); - assert.deepEqual(responsePayload.popular_categories[0], { - id: 'category-0', - category: 'Category 0', - crates_cnt: 0, - created_at: '2010-06-16T21:30:45Z', - description: 'This is the description for the category called "Category 0"', - slug: 'category-0', - }); + assert.equal(responsePayload.num_crates, 20); + assert.equal(responsePayload.num_downloads, 1_419_675); + + assert.equal(responsePayload.popular_categories.length, 10); + assert.deepEqual(responsePayload.popular_categories[0], { + id: 'category-0', + category: 'Category 0', + crates_cnt: 0, + created_at: '2010-06-16T21:30:45Z', + description: 'This is the description for the category called "Category 0"', + slug: 'category-0', + }); - assert.equal(responsePayload.popular_keywords.length, 10); - assert.deepEqual(responsePayload.popular_keywords[0], { - id: 'keyword-1', - crates_cnt: 0, - keyword: 'keyword-1', - }); + assert.equal(responsePayload.popular_keywords.length, 10); + assert.deepEqual(responsePayload.popular_keywords[0], { + id: 'keyword-1', + crates_cnt: 0, + keyword: 'keyword-1', }); }); }); diff --git a/tests/mirage/teams-test.js b/tests/mirage/teams-test.js deleted file mode 100644 index a40701baacf..00000000000 --- a/tests/mirage/teams-test.js +++ /dev/null @@ -1,40 +0,0 @@ -import { module, test } from 'qunit'; - -import fetch from 'fetch'; - -import { setupTest } from 'cargo/tests/helpers'; - -import setupMirage from '../helpers/setup-mirage'; - -module('Mirage | Teams', function (hooks) { - setupTest(hooks); - setupMirage(hooks); - - module('GET /api/v1/teams/:id', function () { - test('returns 404 for unknown teams', async function (assert) { - let response = await fetch('/api/v1/teams/foo'); - assert.equal(response.status, 404); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { errors: [{ detail: 'Not Found' }] }); - }); - - test('returns a team object for known teams', async function (assert) { - let team = this.server.create('team', { name: 'maintainers' }); - - let response = await fetch(`/api/v1/teams/${team.login}`); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - team: { - id: 1, - avatar: 'https://avatars1.githubusercontent.com/u/14631425?v=4', - login: 'github:rust-lang:maintainers', - name: 'maintainers', - url: 'https://github.com/rust-lang', - }, - }); - }); - }); -}); diff --git a/tests/mirage/teams/get-by-id-test.js b/tests/mirage/teams/get-by-id-test.js new file mode 100644 index 00000000000..89ec9d277bb --- /dev/null +++ b/tests/mirage/teams/get-by-id-test.js @@ -0,0 +1,33 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; + +import { setupTest } from '../../helpers'; +import setupMirage from '../../helpers/setup-mirage'; + +module('Mirage | GET /api/v1/teams/:id', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('returns 404 for unknown teams', async function (assert) { + let response = await fetch('/api/v1/teams/foo'); + assert.equal(response.status, 404); + assert.deepEqual(await response.json(), { errors: [{ detail: 'Not Found' }] }); + }); + + test('returns a team object for known teams', async function (assert) { + let team = this.server.create('team', { name: 'maintainers' }); + + let response = await fetch(`/api/v1/teams/${team.login}`); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + team: { + id: 1, + avatar: 'https://avatars1.githubusercontent.com/u/14631425?v=4', + login: 'github:rust-lang:maintainers', + name: 'maintainers', + url: 'https://github.com/rust-lang', + }, + }); + }); +}); diff --git a/tests/mirage/users-test.js b/tests/mirage/users-test.js deleted file mode 100644 index 4cda924eb24..00000000000 --- a/tests/mirage/users-test.js +++ /dev/null @@ -1,152 +0,0 @@ -import { module, test } from 'qunit'; - -import fetch from 'fetch'; - -import { setupTest } from 'cargo/tests/helpers'; - -import setupMirage from '../helpers/setup-mirage'; - -module('Mirage | Users', function (hooks) { - setupTest(hooks); - setupMirage(hooks); - - module('GET /api/v1/users/:id', function () { - test('returns 404 for unknown users', async function (assert) { - let response = await fetch('/api/v1/users/foo'); - assert.equal(response.status, 404); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { errors: [{ detail: 'Not Found' }] }); - }); - - test('returns a user object for known users', async function (assert) { - let user = this.server.create('user', { name: 'John Doe' }); - - let response = await fetch(`/api/v1/users/${user.login}`); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { - user: { - id: 1, - avatar: 'https://avatars1.githubusercontent.com/u/14631425?v=4', - login: 'john-doe', - name: 'John Doe', - url: 'https://github.com/john-doe', - }, - }); - }); - }); - - module('PUT /api/v1/users/:id', function () { - test('updates the user with a new email address', async function (assert) { - let user = this.server.create('user', { email: 'old@email.com' }); - this.server.create('mirage-session', { user }); - - let body = JSON.stringify({ user: { email: 'new@email.com' } }); - let response = await fetch(`/api/v1/users/${user.id}`, { method: 'PUT', body }); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { ok: true }); - - user.reload(); - assert.strictEqual(user.email, 'new@email.com'); - assert.strictEqual(user.emailVerified, false); - assert.strictEqual(user.emailVerificationToken, 'secret123'); - }); - - test('returns 403 when not logged in', async function (assert) { - let user = this.server.create('user', { email: 'old@email.com' }); - - let body = JSON.stringify({ user: { email: 'new@email.com' } }); - let response = await fetch(`/api/v1/users/${user.id}`, { method: 'PUT', body }); - assert.equal(response.status, 403); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { errors: [{ detail: 'must be logged in to perform that action' }] }); - - user.reload(); - assert.strictEqual(user.email, 'old@email.com'); - }); - - test('returns 400 when requesting the wrong user id', async function (assert) { - let user = this.server.create('user', { email: 'old@email.com' }); - this.server.create('mirage-session', { user }); - - let body = JSON.stringify({ user: { email: 'new@email.com' } }); - let response = await fetch(`/api/v1/users/wrong-id`, { method: 'PUT', body }); - assert.equal(response.status, 400); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { errors: [{ detail: 'current user does not match requested user' }] }); - - user.reload(); - assert.strictEqual(user.email, 'old@email.com'); - }); - - test('returns 400 when sending an invalid payload', async function (assert) { - let user = this.server.create('user', { email: 'old@email.com' }); - this.server.create('mirage-session', { user }); - - let body = JSON.stringify({}); - let response = await fetch(`/api/v1/users/${user.id}`, { method: 'PUT', body }); - assert.equal(response.status, 400); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { errors: [{ detail: 'invalid json request' }] }); - - user.reload(); - assert.strictEqual(user.email, 'old@email.com'); - }); - - test('returns 400 when sending an empty email address', async function (assert) { - let user = this.server.create('user', { email: 'old@email.com' }); - this.server.create('mirage-session', { user }); - - let body = JSON.stringify({ user: { email: '' } }); - let response = await fetch(`/api/v1/users/${user.id}`, { method: 'PUT', body }); - assert.equal(response.status, 400); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { errors: [{ detail: 'empty email rejected' }] }); - - user.reload(); - assert.strictEqual(user.email, 'old@email.com'); - }); - }); - - module('PUT /api/v1/users/:id/resend', function () { - test('returns `ok`', async function (assert) { - let user = this.server.create('user'); - this.server.create('mirage-session', { user }); - - let response = await fetch(`/api/v1/users/${user.id}/resend`, { method: 'PUT' }); - assert.equal(response.status, 200); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { ok: true }); - }); - - test('returns 403 when not logged in', async function (assert) { - let user = this.server.create('user'); - - let response = await fetch(`/api/v1/users/${user.id}/resend`, { method: 'PUT' }); - assert.equal(response.status, 403); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { errors: [{ detail: 'must be logged in to perform that action' }] }); - }); - - test('returns 400 when requesting the wrong user id', async function (assert) { - let user = this.server.create('user'); - this.server.create('mirage-session', { user }); - - let response = await fetch(`/api/v1/users/wrong-id/resend`, { method: 'PUT' }); - assert.equal(response.status, 400); - - let responsePayload = await response.json(); - assert.deepEqual(responsePayload, { errors: [{ detail: 'current user does not match requested user' }] }); - }); - }); -}); diff --git a/tests/mirage/users/get-by-id-test.js b/tests/mirage/users/get-by-id-test.js new file mode 100644 index 00000000000..e75e243e46d --- /dev/null +++ b/tests/mirage/users/get-by-id-test.js @@ -0,0 +1,33 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; + +import { setupTest } from '../../helpers'; +import setupMirage from '../../helpers/setup-mirage'; + +module('Mirage | GET /api/v1/users/:id', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('returns 404 for unknown users', async function (assert) { + let response = await fetch('/api/v1/users/foo'); + assert.equal(response.status, 404); + assert.deepEqual(await response.json(), { errors: [{ detail: 'Not Found' }] }); + }); + + test('returns a user object for known users', async function (assert) { + let user = this.server.create('user', { name: 'John Doe' }); + + let response = await fetch(`/api/v1/users/${user.login}`); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + user: { + id: 1, + avatar: 'https://avatars1.githubusercontent.com/u/14631425?v=4', + login: 'john-doe', + name: 'John Doe', + url: 'https://github.com/john-doe', + }, + }); + }); +}); diff --git a/tests/mirage/users/resend-by-id-test.js b/tests/mirage/users/resend-by-id-test.js new file mode 100644 index 00000000000..e60c5c53ce4 --- /dev/null +++ b/tests/mirage/users/resend-by-id-test.js @@ -0,0 +1,37 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; + +import { setupTest } from '../../helpers'; +import setupMirage from '../../helpers/setup-mirage'; + +module('Mirage | PUT /api/v1/users/:id/resend', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('returns `ok`', async function (assert) { + let user = this.server.create('user'); + this.server.create('mirage-session', { user }); + + let response = await fetch(`/api/v1/users/${user.id}/resend`, { method: 'PUT' }); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { ok: true }); + }); + + test('returns 403 when not logged in', async function (assert) { + let user = this.server.create('user'); + + let response = await fetch(`/api/v1/users/${user.id}/resend`, { method: 'PUT' }); + assert.equal(response.status, 403); + assert.deepEqual(await response.json(), { errors: [{ detail: 'must be logged in to perform that action' }] }); + }); + + test('returns 400 when requesting the wrong user id', async function (assert) { + let user = this.server.create('user'); + this.server.create('mirage-session', { user }); + + let response = await fetch(`/api/v1/users/wrong-id/resend`, { method: 'PUT' }); + assert.equal(response.status, 400); + assert.deepEqual(await response.json(), { errors: [{ detail: 'current user does not match requested user' }] }); + }); +}); diff --git a/tests/mirage/users/update-by-id-test.js b/tests/mirage/users/update-by-id-test.js new file mode 100644 index 00000000000..a09d74e8be9 --- /dev/null +++ b/tests/mirage/users/update-by-id-test.js @@ -0,0 +1,77 @@ +import { module, test } from 'qunit'; + +import fetch from 'fetch'; + +import { setupTest } from '../../helpers'; +import setupMirage from '../../helpers/setup-mirage'; + +module('Mirage | PUT /api/v1/users/:id', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('updates the user with a new email address', async function (assert) { + let user = this.server.create('user', { email: 'old@email.com' }); + this.server.create('mirage-session', { user }); + + let body = JSON.stringify({ user: { email: 'new@email.com' } }); + let response = await fetch(`/api/v1/users/${user.id}`, { method: 'PUT', body }); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { ok: true }); + + user.reload(); + assert.strictEqual(user.email, 'new@email.com'); + assert.strictEqual(user.emailVerified, false); + assert.strictEqual(user.emailVerificationToken, 'secret123'); + }); + + test('returns 403 when not logged in', async function (assert) { + let user = this.server.create('user', { email: 'old@email.com' }); + + let body = JSON.stringify({ user: { email: 'new@email.com' } }); + let response = await fetch(`/api/v1/users/${user.id}`, { method: 'PUT', body }); + assert.equal(response.status, 403); + assert.deepEqual(await response.json(), { errors: [{ detail: 'must be logged in to perform that action' }] }); + + user.reload(); + assert.strictEqual(user.email, 'old@email.com'); + }); + + test('returns 400 when requesting the wrong user id', async function (assert) { + let user = this.server.create('user', { email: 'old@email.com' }); + this.server.create('mirage-session', { user }); + + let body = JSON.stringify({ user: { email: 'new@email.com' } }); + let response = await fetch(`/api/v1/users/wrong-id`, { method: 'PUT', body }); + assert.equal(response.status, 400); + assert.deepEqual(await response.json(), { errors: [{ detail: 'current user does not match requested user' }] }); + + user.reload(); + assert.strictEqual(user.email, 'old@email.com'); + }); + + test('returns 400 when sending an invalid payload', async function (assert) { + let user = this.server.create('user', { email: 'old@email.com' }); + this.server.create('mirage-session', { user }); + + let body = JSON.stringify({}); + let response = await fetch(`/api/v1/users/${user.id}`, { method: 'PUT', body }); + assert.equal(response.status, 400); + assert.deepEqual(await response.json(), { errors: [{ detail: 'invalid json request' }] }); + + user.reload(); + assert.strictEqual(user.email, 'old@email.com'); + }); + + test('returns 400 when sending an empty email address', async function (assert) { + let user = this.server.create('user', { email: 'old@email.com' }); + this.server.create('mirage-session', { user }); + + let body = JSON.stringify({ user: { email: '' } }); + let response = await fetch(`/api/v1/users/${user.id}`, { method: 'PUT', body }); + assert.equal(response.status, 400); + assert.deepEqual(await response.json(), { errors: [{ detail: 'empty email rejected' }] }); + + user.reload(); + assert.strictEqual(user.email, 'old@email.com'); + }); +});