Something went wrong. Try again.
Something went wrong. Try again.
12 kB · 421 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422import { test } from '@japa/runner'import { AtUri } from '@atproto/syntax'import testUtils from '@adonisjs/core/services/test_utils'import type { HttpContext } from '@adonisjs/core/http'import type { NextFn } from '@adonisjs/core/types/http'import { DateTime } from 'luxon'import AuthMiddleware from '#middleware/auth_middleware'import KitsService from '#kits/services/kits_service'import Kit from '#kits/models/kit'import { FakeKitsService } from '../helpers/fake_kits_service.js'
class FakeAuthMiddleware extends AuthMiddleware { override async handle(_ctx: HttpContext, next: NextFn) { return next() }}
test.group('Kits Management', (group) => { group.each.setup(() => testUtils.db().truncate())
test('renders the kit create page', async ({ client, swap }) => { swap(AuthMiddleware, new FakeAuthMiddleware()) swap(KitsService, new FakeKitsService())
const response = await client.visit('kits.create').withInertia()
response.assertStatus(200) response.assertInertiaComponent('kits/new') })
test('validates and creates a new kit', async ({ client, db, swap }) => { swap(AuthMiddleware, new FakeAuthMiddleware()) swap(KitsService, new FakeKitsService())
const response = await client .visit('kits.store') .withCsrfToken() .header('referer', '/kits/new') .form({ name: 'HG Aerial', grade: 'HG', scale: '1/144', manufacturer: 'Bandai', status: 'done', }) .redirects(0)
response.assertStatus(302) response.assertHeader('location', '/shelf')
await db.assertHas('kits', { name: 'HG Aerial', grade: 'HG', status: 'done', }) })
test('validates and creates a new kit with an uploaded photo', async ({ client, db, swap, assert, }) => { swap(AuthMiddleware, new FakeAuthMiddleware()) swap(KitsService, new FakeKitsService())
const response = await client .visit('kits.store') .withCsrfToken() .fields({ name: 'RG Hi-Nu Gundam', grade: 'RG', scale: '1/144', status: 'in-progress', }) .file('photo', Buffer.from([0xff, 0xd8, 0xff, 0xe0]), { filename: 'hi-nu.jpg', contentType: 'image/jpeg', }) .redirects(0)
response.assertStatus(302) response.assertHeader('location', '/shelf')
await db.assertHas('kits', { name: 'RG Hi-Nu Gundam', grade: 'RG', status: 'in-progress', }) const kit1 = await Kit.query().where('name', 'RG Hi-Nu Gundam').firstOrFail() assert.deepEqual(kit1.photos, ['bafyfakephotocid']) })
test('validates and creates a new kit with single uploaded photo using photos[]', async ({ client, db, swap, assert, }) => { swap(AuthMiddleware, new FakeAuthMiddleware()) swap(KitsService, new FakeKitsService())
const response = await client .visit('kits.store') .withCsrfToken() .fields({ name: 'MG Nu Gundam', grade: 'MG', scale: '1/100', status: 'in-progress', }) .file('photos[]', Buffer.from([0xff, 0xd8, 0xff, 0xe0]), { filename: 'nu.jpg', contentType: 'image/jpeg', }) .redirects(0)
response.assertStatus(302) response.assertHeader('location', '/shelf')
await db.assertHas('kits', { name: 'MG Nu Gundam' }) const kit2 = await Kit.query().where('name', 'MG Nu Gundam').firstOrFail() assert.deepEqual(kit2.photos, ['bafyfakephotocid0']) })
test('validates and creates a new kit with multiple uploaded photos', async ({ client, db, swap, assert, }) => { swap(AuthMiddleware, new FakeAuthMiddleware()) swap(KitsService, new FakeKitsService())
const response = await client .visit('kits.store') .withCsrfToken() .fields({ name: 'MG Sazabi Ver Ka', grade: 'MG', scale: '1/100', status: 'in-progress', }) .file('photos[0]', Buffer.from([0xff, 0xd8, 0xff, 0xe0]), { filename: 'sazabi-1.jpg', contentType: 'image/jpeg', }) .file('photos[1]', Buffer.from([0xff, 0xd8, 0xff, 0xe0]), { filename: 'sazabi-2.jpg', contentType: 'image/jpeg', }) .redirects(0)
response.assertStatus(302) response.assertHeader('location', '/shelf')
await db.assertHas('kits', { name: 'MG Sazabi Ver Ka', grade: 'MG', status: 'in-progress', }) const kit3 = await Kit.query().where('name', 'MG Sazabi Ver Ka').firstOrFail() assert.deepEqual(kit3.photos, ['bafyfakephotocid0', 'bafyfakephotocid1']) })
test('fails validation when submitting an invalid acquiredAt date', async ({ client, db, swap, }) => { swap(AuthMiddleware, new FakeAuthMiddleware()) swap(KitsService, new FakeKitsService())
const response = await client .visit('kits.store') .withCsrfToken() .form({ name: 'Invalid Date Kit', status: 'backlog', acquiredAt: 'invalid-date', }) .redirects(0)
response.assertStatus(302) response.assertFlashMissing('success') response.assertFlashMessage('errorsBag.E_VALIDATION_ERROR') response.assertFlashMessage('inputErrorsBag.acquiredAt')
await db.assertCount('kits', 0) })
test('fails validation when uploading an invalid file format', async ({ client, db, swap }) => { swap(AuthMiddleware, new FakeAuthMiddleware()) swap(KitsService, new FakeKitsService())
const response = await client .visit('kits.store') .withCsrfToken() .fields({ name: 'Invalid File Kit', status: 'backlog', }) .file('photo', Buffer.from('plain text file'), { filename: 'notes.txt', contentType: 'text/plain', }) .redirects(0)
response.assertStatus(302) response.assertFlashMissing('success') response.assertFlashMessage('errorsBag.E_VALIDATION_ERROR') response.assertFlashMessage('inputErrorsBag.photo')
await db.assertCount('kits', 0) })
test('fails validation when creating kit without required name', async ({ client, db, swap }) => { swap(AuthMiddleware, new FakeAuthMiddleware()) swap(KitsService, new FakeKitsService())
const response = await client .visit('kits.store') .withCsrfToken() .unsafeForm({ grade: 'HG', status: 'done', }) .redirects(0)
response.assertStatus(302) response.assertFlashMissing('success') response.assertFlashMessage('errorsBag.E_VALIDATION_ERROR') response.assertFlashMessage('inputErrorsBag.name')
await db.assertCount('kits', 0) })
test('malformed rkey parameter on edit view returns 404 without calling remote kits service', async ({ client, swap, }) => { let getByRkeyCalled = false class TrackingKitsService extends FakeKitsService { override async getByRkey(rkey: string) { getByRkeyCalled = true return super.getByRkey(rkey) } }
swap(AuthMiddleware, new FakeAuthMiddleware()) swap(KitsService, new TrackingKitsService())
const response = await client.visit('kits.edit', { rkey: 'invalid/rkey!' }).withInertia()
response.assertStatus(404) response.assertInertiaComponent('errors/not_found') if (getByRkeyCalled) { throw new Error('KitsService.getByRkey was unexpectedly called for malformed rkey') } })
test('renders edit kit page with existing kit details', async ({ client, swap }) => { swap(AuthMiddleware, new FakeAuthMiddleware()) swap(KitsService, new FakeKitsService())
const response = await client.visit('kits.edit', { rkey: 'aerial-1' }).withInertia()
response.assertStatus(200) response.assertInertiaComponent('kits/edit') response.assertInertiaPropsContains({ kit: { rkey: 'aerial-1', name: 'HG Aerial', grade: 'HG', scale: '1/144', status: 'done', manufacturer: '', acquiredAt: '', }, }) })
test('valid update payload and rkey updates kit and redirects to shelf', async ({ client, swap, }) => { let updateCalledWith: { rkey: string; data: any } | null = null class TrackingKitsService extends FakeKitsService { override async update(rkey: string, data: any) { updateCalledWith = { rkey, data } return super.update(rkey, data) } }
swap(AuthMiddleware, new FakeAuthMiddleware()) swap(KitsService, new TrackingKitsService())
const response = await client .visit('kits.update', { rkey: 'aerial-1' }) .withCsrfToken() .form({ name: 'Updated Aerial', status: 'done', grade: 'HG', scale: '1/144', }) .redirects(0)
response.assertStatus(302) response.assertHeader('location', '/shelf') response.assertFlashMessage('success', 'Updated "Updated Aerial"') if (!updateCalledWith || (updateCalledWith as any).rkey !== 'aerial-1') { throw new Error('KitsService.update was not called with expected rkey') } })
test('malformed rkey parameter on update fails validation without calling kits service', async ({ client, swap, }) => { let updateCalled = false class TrackingKitsService extends FakeKitsService { override async update(rkey: string, data: any) { updateCalled = true return super.update(rkey, data) } }
swap(AuthMiddleware, new FakeAuthMiddleware()) swap(KitsService, new TrackingKitsService())
const response = await client .visit('kits.update', { rkey: 'invalid/rkey!' }) .withCsrfToken() .form({ name: 'Updated Aerial', status: 'done', }) .redirects(0)
response.assertStatus(302) response.assertFlashMessage('errorsBag.E_VALIDATION_ERROR') if (updateCalled) { throw new Error('KitsService.update was unexpectedly called for malformed rkey') } })
test('malformed rkey parameter on destroy fails validation without calling kits service delete', async ({ client, swap, }) => { let deleteCalled = false class TrackingKitsService extends FakeKitsService { override async delete(rkey: string) { deleteCalled = true return super.delete(rkey) } }
swap(AuthMiddleware, new FakeAuthMiddleware()) swap(KitsService, new TrackingKitsService())
const response = await client .visit('kits.destroy', { rkey: 'invalid/rkey!' }) .withCsrfToken() .redirects(0)
response.assertStatus(302) response.assertFlashMessage('errorsBag.E_VALIDATION_ERROR') if (deleteCalled) { throw new Error('KitsService.delete was unexpectedly called for malformed rkey') } })
test('deletes a kit and removes it from the database', async ({ client, db, swap }) => { swap(AuthMiddleware, new FakeAuthMiddleware()) swap(KitsService, new FakeKitsService())
const kit = await Kit.create({ uri: AtUri.make('did:plc:test', 'build.gunpla.kit', 'aerial-delete').toString(), did: 'did:plc:test', name: 'Kit to delete', status: 'backlog', indexedAt: DateTime.now(), })
await db.assertModelExists(kit)
const response = await client .visit('kits.destroy', { rkey: 'aerial-delete' }) .withCsrfToken() .redirects(0)
response.assertStatus(302) response.assertHeader('location', '/shelf')
await db.assertModelMissing(kit) })
test('first kit creation succeeds and creates profile row', async ({ client, db, swap }) => { swap(AuthMiddleware, new FakeAuthMiddleware()) swap(KitsService, new FakeKitsService())
const response = await client .visit('kits.store') .withCsrfToken() .form({ name: 'HG Aerial', status: 'done', grade: 'HG', scale: '1/144', }) .redirects(0)
response.assertStatus(302) response.assertHeader('location', '/shelf') response.assertFlashMessage('success', 'Kit "HG Aerial" added to your shelf')
await db.assertHas('kits', { name: 'HG Aerial', did: 'did:plc:test' }) })})