diff --git a/tests/mirage/categories-test.js b/tests/mirage/categories-test.js new file mode 100644 index 00000000000..4f51df3540b --- /dev/null +++ b/tests/mirage/categories-test.js @@ -0,0 +1,183 @@ +import { setupTest } from 'ember-qunit'; +import { module, test } from 'qunit'; + +import setupMirage from '../helpers/setup-mirage'; +import fetch from 'fetch'; + +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', + }, + }); + }); + }); + + 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/crates-test.js b/tests/mirage/crates-test.js new file mode 100644 index 00000000000..95828164889 --- /dev/null +++ b/tests/mirage/crates-test.js @@ -0,0 +1,935 @@ +import { setupTest } from 'ember-qunit'; +import { module, test } from 'qunit'; + +import setupMirage from '../helpers/setup-mirage'; +import fetch from 'fetch'; + +module('Mirage | Keywords', 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) { + this.server.create('crate', { name: 'rand' }); + + 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: '1.0.0', + name: 'rand', + newest_version: '1.0.0', + repository: null, + updated_at: '2017-02-24T12:34:56Z', + versions: [], + }, + ], + meta: { + total: 1, + }, + }); + }); + + test('never returns more than 10 results', async function(assert) { + this.server.createList('crate', 25); + + 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) { + this.server.createList('crate', 25, { + name: i => `crate-${String(i + 1).padStart(2, '0')}`, + }); + + 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) { + this.server.create('crate', { name: 'foo' }); + this.server.create('crate', { name: 'bar' }); + this.server.create('crate', { name: '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) { + this.server.create('crate', { name: '123456' }); + this.server.create('crate', { name: '00123' }); + this.server.create('crate', { name: '87654' }); + + 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) { + this.server.create('crate', { name: 'foo' }); + this.server.create('crate', { name: 'bar', _owner_users: [42] }); + this.server.create('crate', { name: 'baz', _owner_users: [13] }); + + let response = await fetch('/api/v1/crates?user_id=42'); + 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) { + this.server.create('crate', { name: 'foo' }); + this.server.create('crate', { name: 'bar', _owner_teams: [42] }); + this.server.create('crate', { name: 'baz', _owner_teams: [13] }); + + let response = await fetch('/api/v1/crates?team_id=42'); + 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) { + this.server.create('crate', { name: 'foo' }); + this.server.create('crate', { name: 'bar', _owner_teams: [42] }); + this.server.create('crate', { name: 'baz', _owner_teams: [13] }); + + let response = await fetch('/api/v1/crates?team_id=42'); + 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); + }); + }); + + 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) { + this.server.create('crate', { name: 'rand' }); + + 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', + name: 'rand', + newest_version: '1.0.0', + repository: null, + updated_at: '2017-02-24T12:34:56Z', + versions: [], + }, + keywords: [], + versions: [], + }); + }); + + test('includes related versions', async function(assert) { + this.server.create('crate', { name: 'rand' }); + this.server.create('version', { crate: 'rand', num: '1.0.0' }); + this.server.create('version', { crate: 'rand', num: '1.1.0' }); + this.server.create('version', { crate: 'rand', 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.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: { + authors: '/api/v1/crates/rand/1.0.0/authors', + dependencies: '/api/v1/crates/rand/1.0.0/dependencies', + version_downloads: '/api/v1/crates/rand/1.0.0/downloads', + }, + num: '1.0.0', + updated_at: '2017-02-24T12:34:56Z', + yanked: false, + }, + { + id: '2', + crate: 'rand', + crate_size: 162963, + created_at: '2010-06-16T21:30:45Z', + dl_path: '/api/v1/crates/rand/1.1.0/download', + downloads: 3702, + license: 'MIT', + links: { + authors: '/api/v1/crates/rand/1.1.0/authors', + dependencies: '/api/v1/crates/rand/1.1.0/dependencies', + version_downloads: '/api/v1/crates/rand/1.1.0/downloads', + }, + num: '1.1.0', + updated_at: '2017-02-24T12:34:56Z', + yanked: false, + }, + { + id: '3', + crate: 'rand', + crate_size: 325926, + created_at: '2010-06-16T21:30:45Z', + dl_path: '/api/v1/crates/rand/1.2.0/download', + downloads: 7404, + license: 'Apache-2.0', + links: { + authors: '/api/v1/crates/rand/1.2.0/authors', + dependencies: '/api/v1/crates/rand/1.2.0/dependencies', + version_downloads: '/api/v1/crates/rand/1.2.0/downloads', + }, + num: '1.2.0', + 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' }); + this.server.create('crate', { name: 'rand', categories: ['no-std'] }); + + let response = await fetch('/api/v1/crates/rand'); + assert.equal(response.status, 200); + + let responsePayload = await response.json(); + assert.deepEqual(responsePayload.categories, [ + { + id: 'no-std', + category: 'no-std', + crates_cnt: 0, + 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' }); + this.server.create('crate', { name: 'rand', keywords: ['no-std'] }); + + let response = await fetch('/api/v1/crates/rand'); + assert.equal(response.status, 200); + + let responsePayload = await response.json(); + assert.deepEqual(responsePayload.keywords, [ + { + crates_cnt: 0, + id: 'no-std', + keyword: 'no-std', + }, + ]); + }); + }); + + 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) { + this.server.create('crate', { name: 'rand' }); + this.server.create('version', { crate: 'rand', num: '1.0.0' }); + this.server.create('version', { crate: 'rand', num: '1.1.0' }); + this.server.create('version', { crate: 'rand', 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: { + authors: '/api/v1/crates/rand/1.0.0/authors', + dependencies: '/api/v1/crates/rand/1.0.0/dependencies', + version_downloads: '/api/v1/crates/rand/1.0.0/downloads', + }, + num: '1.0.0', + updated_at: '2017-02-24T12:34:56Z', + yanked: false, + }, + { + id: '2', + crate: 'rand', + crate_size: 162963, + created_at: '2010-06-16T21:30:45Z', + dl_path: '/api/v1/crates/rand/1.1.0/download', + downloads: 3702, + license: 'MIT', + links: { + authors: '/api/v1/crates/rand/1.1.0/authors', + dependencies: '/api/v1/crates/rand/1.1.0/dependencies', + version_downloads: '/api/v1/crates/rand/1.1.0/downloads', + }, + num: '1.1.0', + updated_at: '2017-02-24T12:34:56Z', + yanked: false, + }, + { + id: '3', + crate: 'rand', + crate_size: 325926, + created_at: '2010-06-16T21:30:45Z', + dl_path: '/api/v1/crates/rand/1.2.0/download', + downloads: 7404, + license: 'Apache-2.0', + links: { + authors: '/api/v1/crates/rand/1.2.0/authors', + dependencies: '/api/v1/crates/rand/1.2.0/dependencies', + version_downloads: '/api/v1/crates/rand/1.2.0/downloads', + }, + num: '1.2.0', + 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) { + this.server.create('crate', { name: 'rand' }); + this.server.create('version', { crate: 'rand', 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 authors = ['John Doe ', 'The Rust Project Developers']; + + this.server.create('crate', { name: 'rand' }); + this.server.create('version', { crate: 'rand', num: '1.0.0', _authors: authors }); + + 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: authors, + }, + 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) { + this.server.create('crate', { name: 'rand' }); + this.server.create('version', { crate: 'rand', 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) { + this.server.create('crate', { name: 'rand' }); + let version = this.server.create('version', { crate: 'rand', num: '1.0.0' }); + this.server.create('dependency', { crate_id: 'foo', version_id: version.id }); + this.server.create('dependency', { crate_id: 'bar', version_id: version.id }); + this.server.create('dependency', { crate_id: 'baz', version_id: version.id }); + + 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) { + this.server.create('crate', { name: 'rand' }); + this.server.create('version', { crate: 'rand', 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) { + this.server.create('crate', { name: 'rand' }); + let version = this.server.create('version', { crate: 'rand', num: '1.0.0' }); + this.server.create('version-download', { version: version.id, date: '2020-01-13' }); + this.server.create('version-download', { version: version.id, date: '2020-01-14' }); + this.server.create('version-download', { version: version.id, date: '2020-01-15' }); + + let response = await fetch('/api/v1/crates/rand/1.0.0/downloads'); + assert.equal(response.status, 200); + + // TODO Remove the `id` properties from the response + let responsePayload = await response.json(); + assert.deepEqual(responsePayload, { + version_downloads: [ + { + id: '1', + date: '2020-01-13', + downloads: 9380, + version: '1', + }, + { + id: '2', + date: '2020-01-14', + downloads: 16415, + version: '1', + }, + { + id: '3', + date: '2020-01-15', + downloads: 23450, + 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' }); + this.server.create('crate', { name: 'rand', _owner_users: [user.id] }); + + 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' }); + this.server.create('crate', { name: 'rand', _owner_teams: [team.id] }); + + 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) { + this.server.create('crate', { name: 'foo' }); + + this.server.create('dependency', { + crate_id: 'foo', + version_id: this.server.create('version', { + crate: this.server.create('crate', { name: 'bar' }).id, + }).id, + }); + + this.server.create('dependency', { + crate_id: 'foo', + version_id: this.server.create('version', { + crate: this.server.create('crate', { name: 'baz' }).id, + }).id, + }); + + 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: { + authors: '/api/v1/crates/bar/1.0.0/authors', + dependencies: '/api/v1/crates/bar/1.0.0/dependencies', + version_downloads: '/api/v1/crates/bar/1.0.0/downloads', + }, + num: '1.0.0', + updated_at: '2017-02-24T12:34:56Z', + yanked: false, + }, + { + id: '2', + crate: 'baz', + crate_size: 162963, + created_at: '2010-06-16T21:30:45Z', + dl_path: '/api/v1/crates/baz/1.0.1/download', + downloads: 3702, + license: 'MIT', + links: { + authors: '/api/v1/crates/baz/1.0.1/authors', + dependencies: '/api/v1/crates/baz/1.0.1/dependencies', + version_downloads: '/api/v1/crates/baz/1.0.1/downloads', + }, + num: '1.0.1', + updated_at: '2017-02-24T12:34:56Z', + yanked: false, + }, + ], + meta: { + total: 2, + }, + }); + }); + + test('never returns more than 10 results', async function(assert) { + this.server.create('crate', { name: 'foo' }); + + this.server.createList('dependency', 25, { + crate_id: 'foo', + version_id: () => + this.server.create('version', { + crate: () => this.server.create('crate', { name: 'bar' }).id, + }).id, + }); + + 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) { + 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].id, + }); + this.server.createList('dependency', versions.length, { + crate_id: 'foo', + version_id: 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) { + this.server.create('crate', { name: 'rand' }); + let versions = this.server.createList('version', 2, { crate: 'rand' }); + this.server.create('version-download', { version: versions[0].id, date: '2020-01-13' }); + this.server.create('version-download', { version: versions[1].id, date: '2020-01-14' }); + this.server.create('version-download', { version: versions[1].id, date: '2020-01-15' }); + + let response = await fetch('/api/v1/crates/rand/downloads'); + assert.equal(response.status, 200); + + // TODO Remove the `id` properties from the response + let responsePayload = await response.json(); + assert.deepEqual(responsePayload, { + version_downloads: [ + { + id: '1', + date: '2020-01-13', + downloads: 9380, + version: '1', + }, + { + id: '2', + date: '2020-01-14', + downloads: 16415, + version: '2', + }, + { + id: '3', + date: '2020-01-15', + downloads: 23450, + version: '2', + }, + ], + meta: { + extra_downloads: [], + }, + }); + }); + }); +}); diff --git a/tests/mirage/keywords-test.js b/tests/mirage/keywords-test.js new file mode 100644 index 00000000000..a5178b0ddff --- /dev/null +++ b/tests/mirage/keywords-test.js @@ -0,0 +1,111 @@ +import { setupTest } from 'ember-qunit'; +import { module, test } from 'qunit'; + +import setupMirage from '../helpers/setup-mirage'; +import fetch from 'fetch'; + +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', + }, + }); + }); + }); +}); diff --git a/tests/mirage/summary-test.js b/tests/mirage/summary-test.js new file mode 100644 index 00000000000..0e732b8a2b1 --- /dev/null +++ b/tests/mirage/summary-test.js @@ -0,0 +1,164 @@ +import { setupTest } from 'ember-qunit'; +import { module, test } from 'qunit'; + +import setupMirage from '../helpers/setup-mirage'; +import fetch from 'fetch'; + +module('Mirage | 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('returns the data for the front page', async function(assert) { + this.server.createList('category', 15); + this.server.createList('keyword', 25); + this.server.createList('crate', 20); + + 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', + 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: 148140, + 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.0', + name: 'crate-4', + 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', + name: 'crate-0', + newest_version: '1.0.0', + 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', + 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, 1419675); + + 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', + }); + }); + }); +}); diff --git a/tests/mirage/teams-test.js b/tests/mirage/teams-test.js new file mode 100644 index 00000000000..c9fe4399300 --- /dev/null +++ b/tests/mirage/teams-test.js @@ -0,0 +1,38 @@ +import { setupTest } from 'ember-qunit'; +import { module, test } from 'qunit'; + +import setupMirage from '../helpers/setup-mirage'; +import fetch from 'fetch'; + +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/users-test.js b/tests/mirage/users-test.js new file mode 100644 index 00000000000..8766a5c98a3 --- /dev/null +++ b/tests/mirage/users-test.js @@ -0,0 +1,38 @@ +import { setupTest } from 'ember-qunit'; +import { module, test } from 'qunit'; + +import setupMirage from '../helpers/setup-mirage'; +import fetch from 'fetch'; + +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', + }, + }); + }); + }); +});