From e63f58e110b75002d66397a6b95d6e7fc6f5df1e Mon Sep 17 00:00:00 2001 From: boufni95 Date: Mon, 25 Mar 2024 17:45:12 +0100 Subject: [PATCH] tests --- package-lock.json | 34 +++++++++++ package.json | 2 + src/index.ts | 44 ++------------ src/services/lnd/index.ts | 2 +- .../lnd/{index.spec.ts => lnd.spec.ts} | 2 +- src/services/main/index.ts | 25 +------- src/services/main/init.ts | 58 +++++++++++++++++++ src/services/main/main.spec.ts | 47 +++++++++++++++ src/services/main/settings.ts | 32 +++++++++- src/services/storage/db.ts | 6 +- src/services/storage/index.ts | 4 +- src/testRunner.ts | 1 - 12 files changed, 187 insertions(+), 70 deletions(-) rename src/services/lnd/{index.spec.ts => lnd.spec.ts} (90%) create mode 100644 src/services/main/init.ts create mode 100644 src/services/main/main.spec.ts diff --git a/package-lock.json b/package-lock.json index 02a51b88..84b972af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "axios": "^0.27.2", "bech32": "^2.0.0", "chai": "^4.3.7", + "chai-string": "^1.5.0", "copyfiles": "^2.4.1", "cors": "^2.8.5", "csv": "^6.3.8", @@ -47,6 +48,7 @@ }, "devDependencies": { "@types/chai": "^4.3.4", + "@types/chai-string": "^1.4.5", "@types/cors": "^2.8.12", "@types/eccrypto": "^1.1.3", "@types/jsonwebtoken": "^8.5.9", @@ -485,6 +487,15 @@ "integrity": "sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==", "dev": true }, + "node_modules/@types/chai-string": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@types/chai-string/-/chai-string-1.4.5.tgz", + "integrity": "sha512-IecXRMSnpUvRnTztdpSdjcmcW7EdNme65bfDCQMi7XrSEPGmyDYYTEfc5fcactWDA6ioSm8o7NUqg9QxjBCCEw==", + "dev": true, + "dependencies": { + "@types/chai": "*" + } + }, "node_modules/@types/connect": { "version": "3.4.35", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", @@ -1127,6 +1138,14 @@ "node": ">=4" } }, + "node_modules/chai-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/chai-string/-/chai-string-1.5.0.tgz", + "integrity": "sha512-sydDC3S3pNAQMYwJrs6dQX0oBQ6KfIPuOZ78n7rocW0eJJlsHPh2t3kwW7xfwYA/1Bf6/arGtSUo16rxR2JFlw==", + "peerDependencies": { + "chai": "^4.1.2" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -4929,6 +4948,15 @@ "integrity": "sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==", "dev": true }, + "@types/chai-string": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@types/chai-string/-/chai-string-1.4.5.tgz", + "integrity": "sha512-IecXRMSnpUvRnTztdpSdjcmcW7EdNme65bfDCQMi7XrSEPGmyDYYTEfc5fcactWDA6ioSm8o7NUqg9QxjBCCEw==", + "dev": true, + "requires": { + "@types/chai": "*" + } + }, "@types/connect": { "version": "3.4.35", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", @@ -5455,6 +5483,12 @@ "type-detect": "^4.0.5" } }, + "chai-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/chai-string/-/chai-string-1.5.0.tgz", + "integrity": "sha512-sydDC3S3pNAQMYwJrs6dQX0oBQ6KfIPuOZ78n7rocW0eJJlsHPh2t3kwW7xfwYA/1Bf6/arGtSUo16rxR2JFlw==", + "requires": {} + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", diff --git a/package.json b/package.json index 042c32c5..76b0e803 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "axios": "^0.27.2", "bech32": "^2.0.0", "chai": "^4.3.7", + "chai-string": "^1.5.0", "copyfiles": "^2.4.1", "cors": "^2.8.5", "csv": "^6.3.8", @@ -63,6 +64,7 @@ }, "devDependencies": { "@types/chai": "^4.3.4", + "@types/chai-string": "^1.4.5", "@types/cors": "^2.8.12", "@types/eccrypto": "^1.1.3", "@types/jsonwebtoken": "^8.5.9", diff --git a/src/index.ts b/src/index.ts index 3f8f435e..a8ce6e62 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,60 +2,28 @@ import 'dotenv/config' import NewServer from '../proto/autogenerated/ts/express_server.js' import GetServerMethods from './services/serverMethods/index.js' import serverOptions from './auth.js'; -import Main, { LoadMainSettingsFromEnv } from './services/main/index.js' import Storage from './services/storage/index.js' import { LoadNosrtSettingsFromEnv } from './services/nostr/index.js' import nostrMiddleware from './nostrMiddleware.js' import { TypeOrmMigrationRunner } from './services/storage/migrations/runner.js'; import { getLogger } from './services/helpers/logger.js'; +import { initMainHandler } from './services/main/init.js'; +import { LoadMainSettingsFromEnv } from './services/main/settings.js'; const start = async () => { const log = getLogger({}) const mainSettings = LoadMainSettingsFromEnv() - const storageManager = new Storage(mainSettings.storageSettings) - const manualMigration = await TypeOrmMigrationRunner(log, storageManager, mainSettings.storageSettings.dbSettings, process.argv[2]) - if (manualMigration) { - log("migrations run sucessfully, exiting") + const keepOn = await initMainHandler(log, mainSettings) + if (!keepOn) { + log("manual process ended") return } - if (process.argv[2] === 'updateUserBalance') { - await storageManager.userStorage.UpdateUser(process.argv[3], { balance_sats: +process.argv[4] }) - log("user balance updated correctly") - } - if (!mainSettings.skipSanityCheck) { - await storageManager.VerifyEventsLog() - } - const mainHandler = new Main(mainSettings, storageManager) - await mainHandler.lnd.Warmup() + const { apps, mainHandler } = keepOn const serverMethods = GetServerMethods(mainHandler) const nostrSettings = LoadNosrtSettingsFromEnv() - const appsData = await mainHandler.storage.applicationStorage.GetApplications() - const existingWalletApp = await appsData.find(app => app.name === 'wallet' || app.name === 'wallet-test') - if (!existingWalletApp) { - log("no default wallet app found, creating one...") - const newWalletApp = await mainHandler.storage.applicationStorage.AddApplication('wallet', true) - appsData.push(newWalletApp) - } - const apps = await Promise.all(appsData.map(app => { - if (!app.nostr_private_key || !app.nostr_public_key) { // TMP -- - return mainHandler.storage.applicationStorage.GenerateApplicationKeys(app); - } // -- - else { - return { privateKey: app.nostr_private_key, publicKey: app.nostr_public_key, appId: app.app_id, name: app.name } - } - })) const { Send } = nostrMiddleware(serverMethods, mainHandler, { ...nostrSettings, apps }) mainHandler.attachNostrSend(Send) const Server = NewServer(serverMethods, serverOptions(mainHandler)) - if (process.argv[2] === 'unlock') { - const u = process.argv[3] - if (u) { - console.log("unlocking user", u) - await mainHandler.storage.userStorage.UnlockUser(u) - } else { - console.log("no user id found to unlock") - } - } Server.Listen(mainSettings.servicePort) } start() diff --git a/src/services/lnd/index.ts b/src/services/lnd/index.ts index 7443a7d8..3f9e363a 100644 --- a/src/services/lnd/index.ts +++ b/src/services/lnd/index.ts @@ -5,7 +5,7 @@ import { AddressPaidCb, BalanceInfo, DecodedInvoice, HtlcCb, Invoice, InvoicePai import LND from './lnd.js' import MockLnd from './mock.js' import { getLogger } from '../helpers/logger.js' -export const LoadLndSettingsFromEnv = (test = false): LndSettings => { +export const LoadLndSettingsFromEnv = (): LndSettings => { const lndAddr = EnvMustBeNonEmptyString("LND_ADDRESS") const lndCertPath = EnvMustBeNonEmptyString("LND_CERT_PATH") const lndMacaroonPath = EnvMustBeNonEmptyString("LND_MACAROON_PATH") diff --git a/src/services/lnd/index.spec.ts b/src/services/lnd/lnd.spec.ts similarity index 90% rename from src/services/lnd/index.spec.ts rename to src/services/lnd/lnd.spec.ts index 69e6068b..7ec63697 100644 --- a/src/services/lnd/index.spec.ts +++ b/src/services/lnd/lnd.spec.ts @@ -5,7 +5,7 @@ import NewLightningHandler, { LightningHandler, LoadLndSettingsFromEnv } from '. let lnd: LightningHandler export const ignore = true export const setup = async () => { - lnd = NewLightningHandler(LoadLndSettingsFromEnv(true), console.log, console.log, console.log, console.log) + lnd = NewLightningHandler(LoadLndSettingsFromEnv(), console.log, console.log, console.log, console.log) await lnd.Warmup() } export const teardown = () => { diff --git a/src/services/main/index.ts b/src/services/main/index.ts index a4761d49..1caed6be 100644 --- a/src/services/main/index.ts +++ b/src/services/main/index.ts @@ -2,40 +2,19 @@ import crypto from 'crypto' import fetch from "node-fetch" import Storage, { LoadStorageSettingsFromEnv } from '../storage/index.js' import * as Types from '../../../proto/autogenerated/ts/types.js' -import { EnvMustBeInteger, EnvMustBeNonEmptyString } from '../helpers/envParser.js' import ProductManager from './productManager.js' import ApplicationManager from './applicationManager.js' import PaymentManager, { PendingTx } from './paymentManager.js' import { MainSettings } from './settings.js' -import NewLightningHandler, { LoadLndSettingsFromEnv, LightningHandler } from "../lnd/index.js" +import NewLightningHandler, { LightningHandler } from "../lnd/index.js" import { AddressPaidCb, HtlcCb, InvoicePaidCb, NewBlockCb } from "../lnd/settings.js" import { getLogger, PubLogger } from "../helpers/logger.js" import AppUserManager from "./appUserManager.js" import { Application } from '../storage/entity/Application.js' -import { UserReceivingInvoice, ZapInfo } from '../storage/entity/UserReceivingInvoice.js' +import { UserReceivingInvoice } from '../storage/entity/UserReceivingInvoice.js' import { UnsignedEvent } from '../nostr/tools/event.js' import { NostrSend } from '../nostr/handler.js' import MetricsManager from '../metrics/index.js' -import EventsLogManager from '../storage/eventsLog.js' -export const LoadMainSettingsFromEnv = (test = false): MainSettings => { - return { - lndSettings: LoadLndSettingsFromEnv(test), - storageSettings: LoadStorageSettingsFromEnv(test), - jwtSecret: EnvMustBeNonEmptyString("JWT_SECRET"), - incomingTxFee: EnvMustBeInteger("INCOMING_CHAIN_FEE_ROOT_BPS") / 10000, - outgoingTxFee: EnvMustBeInteger("OUTGOING_CHAIN_FEE_ROOT_BPS") / 10000, - incomingAppInvoiceFee: EnvMustBeInteger("INCOMING_INVOICE_FEE_ROOT_BPS") / 10000, - outgoingAppInvoiceFee: EnvMustBeInteger("OUTGOING_INVOICE_FEE_ROOT_BPS") / 10000, - incomingAppUserInvoiceFee: EnvMustBeInteger("INCOMING_INVOICE_FEE_USER_BPS") / 10000, - outgoingAppUserInvoiceFee: EnvMustBeInteger("OUTGOING_INVOICE_FEE_USER_BPS") / 10000, - userToUserFee: EnvMustBeInteger("TX_FEE_INTERNAL_USER_BPS") / 10000, - appToUserFee: EnvMustBeInteger("TX_FEE_INTERNAL_ROOT_BPS") / 10000, - serviceUrl: EnvMustBeNonEmptyString("SERVICE_URL"), - servicePort: EnvMustBeInteger("PORT"), - recordPerformance: process.env.RECORD_PERFORMANCE === 'true' || false, - skipSanityCheck: process.env.SKIP_SANITY_CHECK === 'true' || false, - } -} type UserOperationsSub = { id: string diff --git a/src/services/main/init.ts b/src/services/main/init.ts new file mode 100644 index 00000000..4925ca8d --- /dev/null +++ b/src/services/main/init.ts @@ -0,0 +1,58 @@ +import { PubLogger, getLogger } from "../helpers/logger.js" +import Storage from "../storage/index.js" +import { TypeOrmMigrationRunner } from "../storage/migrations/runner.js" +import Main from "./index.js" +import { MainSettings } from "./settings.js" +export type AppData = { + privateKey: string; + publicKey: string; + appId: string; + name: string; +} +export const initMainHandler = async (log: PubLogger, mainSettings: MainSettings) => { + const storageManager = new Storage(mainSettings.storageSettings) + const manualMigration = await TypeOrmMigrationRunner(log, storageManager, mainSettings.storageSettings.dbSettings, process.argv[2]) + if (manualMigration) { + return + } + if (!mainSettings.skipSanityCheck) { + await storageManager.VerifyEventsLog() + } + const mainHandler = new Main(mainSettings, storageManager) + await mainHandler.lnd.Warmup() + const appsData = await mainHandler.storage.applicationStorage.GetApplications() + const existingWalletApp = await appsData.find(app => app.name === 'wallet' || app.name === 'wallet-test') + if (!existingWalletApp) { + log("no default wallet app found, creating one...") + const newWalletApp = await mainHandler.storage.applicationStorage.AddApplication('wallet', true) + appsData.push(newWalletApp) + } + const apps: AppData[] = await Promise.all(appsData.map(app => { + if (!app.nostr_private_key || !app.nostr_public_key) { // TMP -- + return mainHandler.storage.applicationStorage.GenerateApplicationKeys(app); + } // -- + else { + return { privateKey: app.nostr_private_key, publicKey: app.nostr_public_key, appId: app.app_id, name: app.name } + } + })) + const stop = await processArgs(mainHandler) + if (stop) { + return + } + return { mainHandler, apps } +} + +const processArgs = async (mainHandler: Main) => { + switch (process.argv[2]) { + case 'updateUserBalance': + await mainHandler.storage.userStorage.UpdateUser(process.argv[3], { balance_sats: +process.argv[4] }) + getLogger({ userId: process.argv[3] })(`user balance updated correctly`) + return false + case 'unlock': + await mainHandler.storage.userStorage.UnlockUser(process.argv[3]) + getLogger({ userId: process.argv[3] })(`user unlocked`) + return false + default: + return false + } +} \ No newline at end of file diff --git a/src/services/main/main.spec.ts b/src/services/main/main.spec.ts new file mode 100644 index 00000000..e7b06616 --- /dev/null +++ b/src/services/main/main.spec.ts @@ -0,0 +1,47 @@ +import 'dotenv/config' // TODO - test env +import chai from 'chai' +import { AppData, initMainHandler } from './init.js' +import Main from './index.js' +import { User } from '../storage/entity/User.js' +import { LoadMainSettingsFromEnv, LoadTestSettingsFromEnv, MainSettings } from './settings.js' +import chaiString from 'chai-string' +import { defaultInvoiceExpiry } from '../storage/paymentStorage.js' +chai.use(chaiString) +const expect = chai.expect +export const ignore = false +let main: Main +let app: AppData +let user1: { userId: string, appUserIdentifier: string, appId: string } +let user2: { userId: string, appUserIdentifier: string, appId: string } +export const setup = async () => { + const settings = LoadTestSettingsFromEnv() + const initialized = await initMainHandler(console.log, settings) + if (!initialized) { + throw new Error("failed to initialize main handler") + } + main = initialized.mainHandler + app = initialized.apps[0] + const u1 = await main.applicationManager.AddAppUser(app.appId, { identifier: "user1", balance: 0, fail_if_exists: true }) + const u2 = await main.applicationManager.AddAppUser(app.appId, { identifier: "user2", balance: 2000, fail_if_exists: true }) + user1 = { userId: u1.info.userId, appUserIdentifier: u1.identifier, appId: app.appId } + user2 = { userId: u2.info.userId, appUserIdentifier: u2.identifier, appId: app.appId } +} +export const teardown = () => { + console.log("teardown") +} + +export default async (d: (message: string, failure?: boolean) => void) => { + const application = await main.storage.applicationStorage.GetApplication(app.appId) + const invoice = await main.paymentManager.NewInvoice(user1.userId, { amountSats: 1000, memo: "test" }, { linkedApplication: application, expiry: defaultInvoiceExpiry }) + expect(invoice.invoice).to.startWith("lnbcrtmockin") + d("got the invoice") + + const pay = await main.paymentManager.PayInvoice(user2.userId, { invoice: invoice.invoice, amount: 0 }, application) + expect(pay.amount_paid).to.be.equal(1000) + const u1 = await main.storage.userStorage.GetUser(user1.userId) + const u2 = await main.storage.userStorage.GetUser(user2.userId) + const owner = await main.storage.userStorage.GetUser(application.owner.user_id) + expect(u1.balance_sats).to.be.equal(1000) + expect(u2.balance_sats).to.be.equal(994) + expect(owner.balance_sats).to.be.equal(6) +} \ No newline at end of file diff --git a/src/services/main/settings.ts b/src/services/main/settings.ts index 1502d502..3694c961 100644 --- a/src/services/main/settings.ts +++ b/src/services/main/settings.ts @@ -1,5 +1,7 @@ -import { StorageSettings } from '../storage/index.js' +import { LoadStorageSettingsFromEnv, StorageSettings } from '../storage/index.js' import { LndSettings } from '../lnd/settings.js' +import { LoadLndSettingsFromEnv } from '../lnd/index.js' +import { EnvMustBeInteger, EnvMustBeNonEmptyString } from '../helpers/envParser.js' export type MainSettings = { storageSettings: StorageSettings, lndSettings: LndSettings, @@ -16,5 +18,33 @@ export type MainSettings = { servicePort: number recordPerformance: boolean skipSanityCheck: boolean +} +export const LoadMainSettingsFromEnv = (): MainSettings => { + return { + lndSettings: LoadLndSettingsFromEnv(), + storageSettings: LoadStorageSettingsFromEnv(), + jwtSecret: EnvMustBeNonEmptyString("JWT_SECRET"), + incomingTxFee: EnvMustBeInteger("INCOMING_CHAIN_FEE_ROOT_BPS") / 10000, + outgoingTxFee: EnvMustBeInteger("OUTGOING_CHAIN_FEE_ROOT_BPS") / 10000, + incomingAppInvoiceFee: EnvMustBeInteger("INCOMING_INVOICE_FEE_ROOT_BPS") / 10000, + outgoingAppInvoiceFee: EnvMustBeInteger("OUTGOING_INVOICE_FEE_ROOT_BPS") / 10000, + incomingAppUserInvoiceFee: EnvMustBeInteger("INCOMING_INVOICE_FEE_USER_BPS") / 10000, + outgoingAppUserInvoiceFee: EnvMustBeInteger("OUTGOING_INVOICE_FEE_USER_BPS") / 10000, + userToUserFee: EnvMustBeInteger("TX_FEE_INTERNAL_USER_BPS") / 10000, + appToUserFee: EnvMustBeInteger("TX_FEE_INTERNAL_ROOT_BPS") / 10000, + serviceUrl: EnvMustBeNonEmptyString("SERVICE_URL"), + servicePort: EnvMustBeInteger("PORT"), + recordPerformance: process.env.RECORD_PERFORMANCE === 'true' || false, + skipSanityCheck: process.env.SKIP_SANITY_CHECK === 'true' || false, + } +} + +export const LoadTestSettingsFromEnv = (): MainSettings => { + const settings = LoadMainSettingsFromEnv() + return { + ...settings, + storageSettings: { dbSettings: { ...settings.storageSettings.dbSettings, databaseFile: ":memory:", metricsDatabaseFile: ":memory:" } }, + skipSanityCheck: true + } } \ No newline at end of file diff --git a/src/services/storage/db.ts b/src/services/storage/db.ts index 7fa86a94..dbba9363 100644 --- a/src/services/storage/db.ts +++ b/src/services/storage/db.ts @@ -24,11 +24,11 @@ export type DbSettings = { migrate: boolean metricsDatabaseFile: string } -export const LoadDbSettingsFromEnv = (test = false): DbSettings => { +export const LoadDbSettingsFromEnv = (): DbSettings => { return { - databaseFile: test ? ":memory:" : EnvMustBeNonEmptyString("DATABASE_FILE"), + databaseFile: EnvMustBeNonEmptyString("DATABASE_FILE"), migrate: process.env.MIGRATE_DB === 'true' || false, - metricsDatabaseFile: test ? ":memory" : EnvMustBeNonEmptyString("METRICS_DATABASE_FILE") + metricsDatabaseFile: EnvMustBeNonEmptyString("METRICS_DATABASE_FILE") } } diff --git a/src/services/storage/index.ts b/src/services/storage/index.ts index 512889f9..a55f22b3 100644 --- a/src/services/storage/index.ts +++ b/src/services/storage/index.ts @@ -10,8 +10,8 @@ import EventsLogManager, { LoggedEvent } from "./eventsLog.js"; export type StorageSettings = { dbSettings: DbSettings } -export const LoadStorageSettingsFromEnv = (test = false): StorageSettings => { - return { dbSettings: LoadDbSettingsFromEnv(test) } +export const LoadStorageSettingsFromEnv = (): StorageSettings => { + return { dbSettings: LoadDbSettingsFromEnv() } } export default class { diff --git a/src/testRunner.ts b/src/testRunner.ts index e02947fc..8a9b2d87 100644 --- a/src/testRunner.ts +++ b/src/testRunner.ts @@ -24,7 +24,6 @@ const start = async () => { } - const runTestFile = async (fileName: string, mod: TestModule) => { const d = getDescribe(fileName) if (mod.ignore) {