diff --git a/.vscode/launch.json b/.vscode/launch.json index f2b8f38..def10c4 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -16,6 +16,27 @@ "TS_NODE_SKIP_IGNORE": "true" }, "killBehavior": "polite" - } + }, + { + "type": "node", + "request": "launch", + "name": "Jest", + "program": "${workspaceFolder}/api/node_modules/jest/bin/jest", + "cwd": "${workspaceFolder}/api/", + "args": [ + "--testTimeout=3600000", + "--runInBand", + "--no-cache", + ], + "outputCapture": "std", + "console": "integratedTerminal", + "preLaunchTask": "npm: testenv:run", + "postDebugTask": "npm: testenv:stop", + "env": { + "PGHOST": "localhost", + "PGUSER": "postgres", + "PGPASSWORD": "postgres", + }, + }, ] } diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..3d03aae --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,43 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "npm: testenv:run", + "type": "shell", + "command": "npm run testenv:run -- -d", + "isBackground": true, + "options": { + "cwd": "${workspaceFolder}/api/", + }, + "problemMatcher": { + "pattern": { + "regexp": ".", + "file": 1, + "location": 2, + "message": 3 + }, + "background": { + "activeOnStart": true, + "beginsPattern": ".", + "endsPattern": "." + } + } + }, + { + "label": "npm: testenv:stop", + "type": "shell", + "command": "npm run testenv:stop", + "options": { + "cwd": "${workspaceFolder}/api/", + }, + "presentation": { + "echo": true, + "reveal": "silent", + "focus": false, + "panel": "shared", + "showReuseMessage": true, + "clear": false + } + } + ] +} diff --git a/api/package.json b/api/package.json index f629b42..21320c9 100644 --- a/api/package.json +++ b/api/package.json @@ -14,7 +14,10 @@ "generate:vercel": "npm run generate:git-info && npm run generate:openapi && npm run generate:docs", "lint:eslint": "eslint . --ext .ts,.tsx -f unix", "lint:prettier": "prettier --check src/**/*.ts tests/**/*.ts", - "lint:unused-exports": "ts-unused-exports tsconfig.json --showLineNumber --excludePathsFromReport=util/*" + "lint:unused-exports": "ts-unused-exports tsconfig.json --showLineNumber --excludePathsFromReport=util/*", + "testenv:run": "docker-compose -f ../docker/docker-compose.dev.postgres.yml up", + "testenv:stop": "docker-compose -f ../docker/docker-compose.dev.postgres.yml down -v -t 0", + "testenv:logs": "docker-compose -f ../docker/docker-compose.dev.postgres.yml logs -t -f" }, "author": "Hiro Systems PBC (https://hiro.so)", "license": "Apache 2.0", diff --git a/api/src/api/schemas.ts b/api/src/api/schemas.ts index 12cf13a..06ddb18 100644 --- a/api/src/api/schemas.ts +++ b/api/src/api/schemas.ts @@ -307,6 +307,7 @@ const RuneDetailResponseSchema = Type.Object({ { id: RuneIdResponseSchema, name: RuneNameResponseSchema, + // number: RuneNumberResponseSchema, spaced_name: RuneSpacedNameResponseSchema, }, { title: 'Rune detail', description: 'Details of the rune affected by this activity' } diff --git a/api/tests/api/api.test.ts b/api/tests/api/api.test.ts index 1129944..a21d735 100644 --- a/api/tests/api/api.test.ts +++ b/api/tests/api/api.test.ts @@ -1,13 +1,16 @@ import { ENV } from '../../src/env'; import { PgStore } from '../../src/pg/pg-store'; -import { DbLedgerEntry, DbRune } from '../../src/pg/types'; +import { DbLedgerEntry } from '../../src/pg/types'; import { dropDatabase, insertDbEntry, insertRune, + sampleRune, runMigrations, startTestApiServer, TestFastifyServer, + insertSupply, + sampleLedgerEntry, } from '../helpers'; describe('Etchings', () => { @@ -31,64 +34,23 @@ describe('Etchings', () => { }); test('displays etched rune', async () => { - // '1:0', 0, 'UNCOMMONGOODS', 'UNCOMMON•GOODS', - // '0000000000000000000320283a032748cef8227873ff4872689bf23f1cda83a5', 840000, 0, '', '⧉', 1, - // '340282366920938463463374607431768211455', 840000, 1050000, 0 - const ledgerEntry: DbLedgerEntry = { - rune_id: '1:1', - block_hash: 'sample_block_hash', - block_height: '1', - tx_index: 0, - tx_id: '0', - output: 0, - address: '0', - receiver_address: '0', - amount: '0', - operation: 'etching', - timestamp: 0 - }; - const rune: DbRune = { - id: '1:1', - name: 'Sample Rune Name', - spaced_name: 'Sample•Rune•Name', - number: 1, - block_hash: 'sample_block_hash', - block_height: '10', - tx_index: 0, - tx_id: 'sample_tx_id', - divisibility: 8, - premine: '1000', - symbol: 'SRN', - cenotaph: true, - terms_amount: '1000000', - terms_cap: '5000000', - terms_height_start: null, - terms_height_end: null, - terms_offset_start: null, - terms_offset_end: null, - turbo: false, - minted: '1000', - total_mints: '1500', - burned: '500', - total_burns: '750', - total_operations: '2000', - timestamp: Date.now(), - }; + const rune = sampleRune('1:1', 'Sample Rune'); + const ledgerEntry = sampleLedgerEntry(rune.id); await insertRune(db, rune); - const runes = await fastify.inject({ + const event_index = 0; + await insertDbEntry(db, ledgerEntry, event_index); + await insertSupply(db, rune.id, 1); + const runesResponse = await fastify.inject({ method: 'GET', url: '/runes/v1/etchings', }); - expect(JSON.parse(runes.body).results.not.toHaveLength(0)); - expect(runes.statusCode).toBe(200); - // TODO: ???? - const event_index = 0; - await insertDbEntry(db, ledgerEntry, event_index); - // console.log(runes); + expect(runesResponse.statusCode).toBe(200); + expect(runesResponse.json().results).not.toHaveLength(0); const response = await fastify.inject({ method: 'GET', url: '/runes/v1/etchings/' + ledgerEntry.rune_id, }); expect(response.statusCode).toBe(200); + expect(response.json().name).toEqual(rune.name); }); }); diff --git a/api/tests/helpers.ts b/api/tests/helpers.ts index 3eec929..0cf68f1 100644 --- a/api/tests/helpers.ts +++ b/api/tests/helpers.ts @@ -53,6 +53,59 @@ export async function dropDatabase(db: PgStore) { `; }); } +export function sampleLedgerEntry(rune_id: string, block_height?: string): DbLedgerEntry { + return { + rune_id: '1:1', + block_hash: '0000000000000000000320283a032748cef8227873ff4872689bf23f1cda83a5', + block_height: block_height || '840000', + tx_index: 0, + tx_id: '2bb85f4b004be6da54f766c17c1e855187327112c231ef2ff35ebad0ea67c69e', + output: 0, + address: '0', + receiver_address: '0', + amount: '0', + operation: 'etching', + timestamp: 0, + }; +} + +function toSpacedName(name: string | null): string | null { + if (name === null) { + return null; + } + // should take "Some name" and make it "Some•name" + const words = name.split(' '); + return words.join('•'); +} +export function sampleRune(id: string, name?: string): DbRune { + return { + id: '1:1', + name: name || 'Sample Rune Name', + spaced_name: (name && toSpacedName(name)) || 'Sample•Rune•Name', + number: 1, + block_hash: '0000000000000000000320283a032748cef8227873ff4872689bf23f1cda83a5', + block_height: '840000', + tx_index: 1, + tx_id: '2bb85f4b004be6da54f766c17c1e855187327112c231ef2ff35ebad0ea67c69e', + divisibility: 2, + premine: '1000', + symbol: 'ᚠ', + cenotaph: true, + terms_amount: '100', + terms_cap: '5000000', + terms_height_start: null, + terms_height_end: null, + terms_offset_start: null, + terms_offset_end: null, + turbo: false, + minted: '1000', + total_mints: '1500', + burned: '500', + total_burns: '750', + total_operations: '1', + timestamp: 1713571767, + }; +} export async function insertDbEntry( db: PgStore, @@ -86,6 +139,32 @@ export async function insertDbEntry( }); } +export async function insertSupply( + db: PgStore, + rune_id: string, + block_height: number, + minted?: number, + total_mints?: number, + total_operations?: number +): Promise { + await db.sqlWriteTransaction(async sql => { + const burned = 0; + const total_burned = 0; + + await sql` + INSERT INTO supply_changes ( + rune_id, block_height, minted, total_mints, burned, total_burns, total_operations + ) + VALUES ( + + ${rune_id}, ${block_height}, ${minted || 0}, ${ + total_mints || 0 + }, ${burned}, ${total_burned}, ${total_operations || 0} + ) + `; + }); +} + export async function insertRune(db: PgStore, payload: DbRune): Promise { await db.sqlWriteTransaction(async sql => { const { @@ -105,13 +184,6 @@ export async function insertRune(db: PgStore, payload: DbRune): Promise { terms_height_end, } = payload; - // INSERT INTO runes ( - // id, number, name, spaced_name, block_hash, block_height, tx_index, tx_id, symbol, terms_amount, - // terms_cap, terms_height_start, terms_height_end, timestamp - // ) - // '1:0', 0, 'UNCOMMONGOODS', 'UNCOMMON•GOODS', - // '0000000000000000000320283a032748cef8227873ff4872689bf23f1cda83a5', 840000, 0, '', '⧉', 1, - // '340282366920938463463374607431768211455', 840000, 1050000, 0 await sql` INSERT INTO runes ( id, number, name, spaced_name, block_hash, block_height, tx_index, tx_id, symbol, cenotaph, diff --git a/api/tests/setup.ts b/api/tests/setup.ts index 3b7ab9f..2caf110 100644 --- a/api/tests/setup.ts +++ b/api/tests/setup.ts @@ -2,6 +2,4 @@ export default (): void => { process.env.PGDATABASE = 'postgres'; process.env.PGPASSWORD = 'postgres'; - process.env.PGUSER = 'test'; - process.env.PGHOST = 'localhost'; };