From 7d693247c07a18732ac1b2ed21cc8194b31ce0ed Mon Sep 17 00:00:00 2001 From: boufni95 Date: Tue, 11 Mar 2025 22:32:53 +0000 Subject: [PATCH] better runner --- src/services/main/settings.ts | 9 ++- src/tests/testBase.ts | 23 ++++++- src/tests/testRunner.ts | 33 +++++++--- src/tests/testStorage.spec.ts | 116 +++++++++++++++++++--------------- 4 files changed, 119 insertions(+), 62 deletions(-) diff --git a/src/services/main/settings.ts b/src/services/main/settings.ts index e9d7ef7d..5791c7c0 100644 --- a/src/services/main/settings.ts +++ b/src/services/main/settings.ts @@ -78,12 +78,19 @@ export const LoadMainSettingsFromEnv = (): MainSettings => { } } +export const GetTestStorageSettings = (s?: StorageSettings): StorageSettings => { + if (s) { + return { dbSettings: { ...s.dbSettings, databaseFile: ":memory:", metricsDatabaseFile: ":memory:" }, eventLogPath: s.eventLogPath, dataDir: "test-data" } + } + return { dbSettings: { databaseFile: ":memory:", metricsDatabaseFile: ":memory:", migrate: true }, eventLogPath: "logs/eventLogV3Test.csv", dataDir: "test-data" } +} + export const LoadTestSettingsFromEnv = (): TestSettings => { const eventLogPath = `logs/eventLogV3Test${Date.now()}.csv` const settings = LoadMainSettingsFromEnv() return { ...settings, - storageSettings: { dbSettings: { ...settings.storageSettings.dbSettings, databaseFile: ":memory:", metricsDatabaseFile: ":memory:" }, eventLogPath, dataDir: "data" }, + storageSettings: GetTestStorageSettings(settings.storageSettings), lndSettings: { ...settings.lndSettings, otherNode: { diff --git a/src/tests/testBase.ts b/src/tests/testBase.ts index dcad8364..a3b716b7 100644 --- a/src/tests/testBase.ts +++ b/src/tests/testBase.ts @@ -4,7 +4,7 @@ import { AppData, initMainHandler } from '../services/main/init.js' import Main from '../services/main/index.js' import Storage from '../services/storage/index.js' import { User } from '../services/storage/entity/User.js' -import { LoadMainSettingsFromEnv, LoadTestSettingsFromEnv, MainSettings } from '../services/main/settings.js' +import { GetTestStorageSettings, LoadMainSettingsFromEnv, LoadTestSettingsFromEnv, MainSettings } from '../services/main/settings.js' import chaiString from 'chai-string' import { defaultInvoiceExpiry } from '../services/storage/paymentStorage.js' import SanityChecker from '../services/main/sanityChecker.js' @@ -35,6 +35,27 @@ export type TestBase = { d: Describe } +export type StorageTestBase = { + expect: Chai.ExpectStatic; + storage: Storage + d: Describe +} + +export const setupStorageTest = async (d: Describe): Promise => { + const settings = GetTestStorageSettings() + const storageManager = new Storage(settings) + await storageManager.Connect(console.log) + return { + expect, + storage: storageManager, + d + } +} + +export const teardownStorageTest = async (T: StorageTestBase) => { + T.storage.Stop() +} + export const SetupTest = async (d: Describe): Promise => { const settings = LoadTestSettingsFromEnv() const initialized = await initMainHandler(getLogger({ component: "mainForTest" }), settings) diff --git a/src/tests/testRunner.ts b/src/tests/testRunner.ts index 754d8c66..d12f0cbc 100644 --- a/src/tests/testRunner.ts +++ b/src/tests/testRunner.ts @@ -1,11 +1,12 @@ //import whyIsNodeRunning from 'why-is-node-running' import { globby } from 'globby' import { setupNetwork } from './networkSetup.js' -import { Describe, SetupTest, teardown, TestBase } from './testBase.js' +import { Describe, SetupTest, teardown, TestBase, StorageTestBase, setupStorageTest, teardownStorageTest } from './testBase.js' type TestModule = { ignore?: boolean dev?: boolean - default: (T: TestBase) => Promise + requires?: 'storage' | '*' + default: (T: TestBase | StorageTestBase) => Promise } let failures = 0 const getDescribe = (fileName: string): Describe => { @@ -20,7 +21,6 @@ const getDescribe = (fileName: string): Describe => { } const start = async () => { - await setupNetwork() const files = await globby(["**/*.spec.js", "!**/node_modules/**"]) const modules: { file: string, module: TestModule }[] = [] let devModule = -1 @@ -37,7 +37,13 @@ const start = async () => { } if (devModule !== -1) { console.log("running dev module") - await runTestFile(modules[devModule].file, modules[devModule].module) + const { file, module } = modules[devModule] + if (module.requires === 'storage') { + console.log("dev module requires only storage, skipping network setup") + } else { + await setupNetwork() + } + await runTestFile(file, module) } else { console.log("running all tests") for (const { file, module } of modules) { @@ -65,17 +71,28 @@ const runTestFile = async (fileName: string, mod: TestModule) => { if (mod.dev) { d("-----running only this file-----") } - const T = await SetupTest(d) + let T: TestBase | StorageTestBase + if (mod.requires === 'storage') { + d("-----requires only storage-----") + T = await setupStorageTest(d) + } else { + d("-----requires all-----") + T = await SetupTest(d) + } try { d("test starting") await mod.default(T) - d("test finished") - await teardown(T) } catch (e: any) { d(e, true) d("test crashed", true) - await teardown(T) + } finally { + if (mod.requires === 'storage') { + await teardownStorageTest(T as StorageTestBase) + } else { + await teardown(T as TestBase) + } } + d("test finished") if (mod.dev) { d("dev mod is not allowed to in CI, failing for precaution", true) } diff --git a/src/tests/testStorage.spec.ts b/src/tests/testStorage.spec.ts index cb2b783c..35715f95 100644 --- a/src/tests/testStorage.spec.ts +++ b/src/tests/testStorage.spec.ts @@ -1,101 +1,113 @@ import { User } from '../services/storage/entity/User.js' import { defaultInvoiceExpiry } from '../services/storage/paymentStorage.js' -import { runSanityCheck, safelySetUserBalance, TestBase } from './testBase.js' +import { runSanityCheck, safelySetUserBalance, StorageTestBase, TestBase } from './testBase.js' import { FindOptionsWhere } from 'typeorm' export const ignore = false -export const dev = false -export const storageOnly = false +export const dev = true +export const requires = 'storage' -export default async (T: TestBase) => { - T.main.storage.dbs.setDebug(true) - await testCanReadUser(T) - await testConcurrentReads(T) - await testTransactionIsolation(T) +export default async (T: StorageTestBase) => { + const u = await testCanCreateUser(T) + await testCanReadUser(T, u) + await testConcurrentReads(T, u) + T.storage.dbs.setDebug(true) + await testTransactionIsolation(T, u) + T.storage.dbs.setDebug(false) await testUserCRUD(T) - await testErrorHandling(T) + await testErrorHandling(T, u) } -const testCanReadUser = async (T: TestBase) => { - T.d('Starting testCanReadUser') - const u = await T.main.storage.dbs.FindOne('User', { where: { user_id: T.user1.userId } }) +const testCanCreateUser = async (T: StorageTestBase) => { + T.d('Starting testCanCreateUser') + const u = await T.storage.dbs.CreateAndSave('User', { + user_id: 'test-user-' + Date.now(), + balance_sats: 0, + locked: false, + }) T.expect(u).to.not.be.equal(null) - T.expect(u?.user_id).to.be.equal(T.user1.userId) + T.d('Finished testCanCreateUser') + return u +} + +const testCanReadUser = async (T: StorageTestBase, user: User) => { + T.d('Starting testCanReadUser') + const u = await T.storage.dbs.FindOne('User', { where: { user_id: user.user_id } }) + T.expect(u).to.not.be.equal(null) + T.expect(u?.user_id).to.be.equal(user.user_id) T.d('Finished testCanReadUser') } -const testConcurrentReads = async (T: TestBase) => { +const testConcurrentReads = async (T: StorageTestBase, user: User) => { T.d('Starting testConcurrentReads') // Test multiple concurrent read operations const promises = [ - T.main.storage.dbs.FindOne('User', { where: { user_id: T.user1.userId } }), - T.main.storage.dbs.FindOne('User', { where: { user_id: T.user2.userId } }), - T.main.storage.dbs.Find('User', {}) + T.storage.dbs.FindOne('User', { where: { user_id: user.user_id } }), + T.storage.dbs.Find('User', {}) ] as const const results = await Promise.all(promises) // Type assertions to handle possible null values - const [user1, user2, allUsers] = results + const [user1, allUsers] = results - T.expect(user1?.user_id).to.be.equal(T.user1.userId) - T.expect(user2?.user_id).to.be.equal(T.user2.userId) + T.expect(user1?.user_id).to.be.equal(user.user_id) T.expect(allUsers).to.not.be.equal(null) - T.expect(allUsers.length).to.be.greaterThan(1) + T.expect(allUsers.length).to.be.equal(1) T.d('Finished testConcurrentReads') } -const testTransactionIsolation = async (T: TestBase) => { +const testTransactionIsolation = async (T: StorageTestBase, user: User) => { T.d('Starting testTransactionIsolation') // Start a transaction // Check initial balance before transaction - const userBefore = await T.main.storage.dbs.FindOne('User', { - where: { user_id: T.user1.userId } + const userBefore = await T.storage.dbs.FindOne('User', { + where: { user_id: user.user_id } }) T.expect(userBefore?.balance_sats).to.not.equal(1000, 'User should not start with balance of 1000') - const txId = await T.main.storage.dbs.StartTx('test-transaction') + const txId = await T.storage.dbs.StartTx('test-transaction') try { // Update user balance in transaction const initialBalance = 1000 - const where: FindOptionsWhere = { user_id: T.user1.userId } + const where: FindOptionsWhere = { user_id: user.user_id } - await T.main.storage.dbs.Update('User', + await T.storage.dbs.Update('User', where, { balance_sats: initialBalance }, txId ) // Verify balance is updated in transaction - const userInTx = await T.main.storage.dbs.FindOne('User', + const userInTx = await T.storage.dbs.FindOne('User', { where }, txId ) T.expect(userInTx?.balance_sats).to.be.equal(initialBalance) // Verify balance is not visible outside transaction - const userOutsideTx = await T.main.storage.dbs.FindOne('User', + const userOutsideTx = await T.storage.dbs.FindOne('User', { where } ) T.expect(userOutsideTx?.balance_sats).to.not.equal(initialBalance) // Commit the transaction - await T.main.storage.dbs.EndTx(txId, true, null) + await T.storage.dbs.EndTx(txId, true, null) // Verify balance is now visible - const userAfterCommit = await T.main.storage.dbs.FindOne('User', + const userAfterCommit = await T.storage.dbs.FindOne('User', { where } ) T.expect(userAfterCommit?.balance_sats).to.be.equal(initialBalance) } catch (error) { // Rollback on error - await T.main.storage.dbs.EndTx(txId, false, error instanceof Error ? error.message : 'Unknown error') + await T.storage.dbs.EndTx(txId, false, error instanceof Error ? error.message : 'Unknown error') throw error } T.d('Finished testTransactionIsolation') } -const testUserCRUD = async (T: TestBase) => { +const testUserCRUD = async (T: StorageTestBase) => { T.d('Starting testUserCRUD') // Create const newUser = { @@ -104,10 +116,10 @@ const testUserCRUD = async (T: TestBase) => { locked: false, } - await T.main.storage.dbs.CreateAndSave('User', newUser) + await T.storage.dbs.CreateAndSave('User', newUser) // Read - const createdUser = await T.main.storage.dbs.FindOne('User', + const createdUser = await T.storage.dbs.FindOne('User', { where: { user_id: newUser.user_id } as FindOptionsWhere } ) T.expect(createdUser).to.not.be.equal(null) @@ -115,40 +127,40 @@ const testUserCRUD = async (T: TestBase) => { // Update const newBalance = 500 - await T.main.storage.dbs.Update('User', + await T.storage.dbs.Update('User', { user_id: newUser.user_id } as FindOptionsWhere, { balance_sats: newBalance } ) - const updatedUser = await T.main.storage.dbs.FindOne('User', + const updatedUser = await T.storage.dbs.FindOne('User', { where: { user_id: newUser.user_id } as FindOptionsWhere } ) T.expect(updatedUser?.balance_sats).to.be.equal(newBalance) // Delete - await T.main.storage.dbs.Delete('User', + await T.storage.dbs.Delete('User', { user_id: newUser.user_id } as FindOptionsWhere ) - const deletedUser = await T.main.storage.dbs.FindOne('User', + const deletedUser = await T.storage.dbs.FindOne('User', { where: { user_id: newUser.user_id } as FindOptionsWhere } ) T.expect(deletedUser).to.be.equal(null) T.d('Finished testUserCRUD') } -const testErrorHandling = async (T: TestBase) => { +const testErrorHandling = async (T: StorageTestBase, user: User) => { T.d('Starting testErrorHandling') // Test null result (not an error) - const nonExistentUser = await T.main.storage.dbs.FindOne('User', + const nonExistentUser = await T.storage.dbs.FindOne('User', { where: { user_id: 'does-not-exist' } as FindOptionsWhere } ) T.expect(nonExistentUser).to.be.equal(null) // Test actual error case - invalid column name should throw an error try { - await T.main.storage.dbs.Update('User', - { user_id: T.user1.userId } as FindOptionsWhere, + await T.storage.dbs.Update('User', + { user_id: user.user_id } as FindOptionsWhere, { nonexistent_column: 'value' } as any ) T.expect.fail('Should have thrown an error') @@ -157,24 +169,24 @@ const testErrorHandling = async (T: TestBase) => { } // Test transaction rollback - const txId = await T.main.storage.dbs.StartTx('test-error-transaction') + const txId = await T.storage.dbs.StartTx('test-error-transaction') try { // Try to update with an invalid column which should cause an error - await T.main.storage.dbs.Update('User', - { user_id: T.user1.userId } as FindOptionsWhere, + await T.storage.dbs.Update('User', + { user_id: user.user_id } as FindOptionsWhere, { invalid_column: 'test' } as any, txId ) - await T.main.storage.dbs.EndTx(txId, false, 'Rolling back test transaction') + await T.storage.dbs.EndTx(txId, false, 'Rolling back test transaction') // Verify no changes were made - const user = await T.main.storage.dbs.FindOne('User', - { where: { user_id: T.user1.userId } } + const userAfterTx = await T.storage.dbs.FindOne('User', + { where: { user_id: user.user_id } } ) - T.expect(user).to.not.be.equal(null) - T.expect((user as any).invalid_column).to.be.equal(undefined) + T.expect(userAfterTx).to.not.be.equal(null) + T.expect((userAfterTx as any).invalid_column).to.be.equal(undefined) } catch (error) { - await T.main.storage.dbs.EndTx(txId, false, error instanceof Error ? error.message : 'Unknown error') + await T.storage.dbs.EndTx(txId, false, error instanceof Error ? error.message : 'Unknown error') T.expect(error).to.not.be.equal(null) } T.d('Finished testErrorHandling')