Merge pull request #666 from shocknet/moar-tests
better settings, 3rd node
This commit is contained in:
commit
f8e05be6de
19 changed files with 152 additions and 102 deletions
|
|
@ -4,14 +4,14 @@
|
|||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": " tsc && node build/src/tests/testRunner.js",
|
||||
"start": "tsc && node build/src/index.js",
|
||||
"clean": "rimraf build",
|
||||
"test": "npm run clean && tsc && node build/src/tests/testRunner.js",
|
||||
"start": "npm run clean && tsc && node build/src/index.js",
|
||||
"start:ci": "git reset --hard && git pull && npm run start",
|
||||
"build_autogenerated": "cd proto && rimraf autogenerated && protoc -I ./service --pub_out=. service/*",
|
||||
"build_lnd_client_1": "cd proto && protoc -I ./others --plugin=.\\node_modules\\.bin\\protoc-gen-ts_proto.cmd --ts_proto_out=./lnd --ts_proto_opt=esModuleInterop=true others/* ",
|
||||
"build_lnd_client": "cd proto && rimraf lnd/* && npx protoc --ts_out ./lnd --ts_opt long_type_string --proto_path others others/* ",
|
||||
"typeorm": "typeorm-ts-node-commonjs",
|
||||
"aa": "tsc && cd build && node src/services/nostr/index.js"
|
||||
"typeorm": "typeorm-ts-node-commonjs"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
|||
|
|
@ -31,45 +31,3 @@ export default (serverMethods: Types.ServerMethods, mainHandler: Main, nostrSett
|
|||
})
|
||||
return { Stop: () => nostr.Stop, Send: (...args) => nostr.Send(...args) }
|
||||
}
|
||||
|
||||
/*
|
||||
export default (serverMethods: Types.ServerMethods, mainHandler: Main, nostrSettings: NostrSettings): Nostr => {
|
||||
// TODO: - move to codegen
|
||||
const nostr = new Nostr(nostrSettings,
|
||||
async (event) => {
|
||||
if (!nostrSettings.allowedPubs.includes(event.pub)) {
|
||||
console.log("nostr pub not allowed")
|
||||
return
|
||||
}
|
||||
let nostrUser = await mainHandler.storage.FindNostrUser(event.pub)
|
||||
if (!nostrUser) {
|
||||
nostrUser = await mainHandler.storage.AddNostrUser(event.pub)
|
||||
}
|
||||
let j: EventRequest
|
||||
try {
|
||||
j = JSON.parse(event.content)
|
||||
} catch {
|
||||
console.error("invalid json event received", event.content)
|
||||
return
|
||||
}
|
||||
if (handledRequests.includes(j.requestId)) {
|
||||
console.log("request already handled")
|
||||
return
|
||||
}
|
||||
handledRequests.push(j.requestId)
|
||||
switch (j.method) {
|
||||
case '/api/user/chain/new':
|
||||
const error = Types.NewAddressRequestValidate(j.body)
|
||||
if (error !== null) {
|
||||
console.error("invalid request from", event.pub, j)// TODO: dont dox
|
||||
return // TODO: respond
|
||||
}
|
||||
if (!serverMethods.NewAddress) {
|
||||
throw new Error("unimplemented NewInvoice")
|
||||
}
|
||||
const res = await serverMethods.NewAddress({ user_id: nostrUser.user.user_id }, j.body)
|
||||
nostr.Send(event.pub, JSON.stringify({ ...res, requestId: j.requestId }))
|
||||
}
|
||||
})
|
||||
return nostr
|
||||
}*/
|
||||
|
|
@ -44,7 +44,7 @@ export const getLogger = (params: LoggerParams): PubLogger => {
|
|||
if (params.userId) {
|
||||
toLog.push(params.userId)
|
||||
}
|
||||
const parsed = message.map(m => typeof m === 'object' ? JSON.stringify(m) : m)
|
||||
const parsed = message.map(m => typeof m === 'object' ? JSON.stringify(m, (_, v) => typeof v === 'bigint' ? v.toString() : v) : m)
|
||||
const final = `${toLog.join(" ")} >> ${parsed.join(" ")}`
|
||||
console.log(final)
|
||||
writers.forEach(w => w(final))
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export const LoadLndSettingsFromEnv = (): LndSettings => {
|
|||
const feeRateLimit = EnvMustBeInteger("OUTBOUND_MAX_FEE_BPS") / 10000
|
||||
const feeFixedLimit = EnvMustBeInteger("OUTBOUND_MAX_FEE_EXTRA_SATS")
|
||||
const mockLnd = EnvCanBeBoolean("MOCK_LND")
|
||||
return { lndAddr, lndCertPath, lndMacaroonPath, feeRateLimit, feeFixedLimit, mockLnd, otherLndAddr: "", otherLndCertPath: "", otherLndMacaroonPath: "" }
|
||||
return { mainNode: { lndAddr, lndCertPath, lndMacaroonPath }, feeRateLimit, feeFixedLimit, mockLnd }
|
||||
}
|
||||
export interface LightningHandler {
|
||||
Stop(): void
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ export default class {
|
|||
this.invoicePaidCb = invoicePaidCb
|
||||
this.newBlockCb = newBlockCb
|
||||
this.htlcCb = htlcCb
|
||||
const { lndAddr, lndCertPath, lndMacaroonPath } = this.settings
|
||||
const { lndAddr, lndCertPath, lndMacaroonPath } = this.settings.mainNode
|
||||
const lndCert = fs.readFileSync(lndCertPath);
|
||||
const macaroon = fs.readFileSync(lndMacaroonPath).toString('hex');
|
||||
const sslCreds = credentials.createSsl(lndCert);
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
import { HtlcEvent } from "../../../proto/lnd/router"
|
||||
|
||||
export type LndSettings = {
|
||||
export type NodeSettings = {
|
||||
lndAddr: string
|
||||
lndCertPath: string
|
||||
lndMacaroonPath: string
|
||||
}
|
||||
export type LndSettings = {
|
||||
mainNode: NodeSettings
|
||||
feeRateLimit: number
|
||||
feeFixedLimit: number
|
||||
mockLnd: boolean
|
||||
|
||||
otherLndAddr: string
|
||||
otherLndCertPath: string
|
||||
otherLndMacaroonPath: string
|
||||
otherNode?: NodeSettings
|
||||
thirdNode?: NodeSettings
|
||||
}
|
||||
type TxOutput = {
|
||||
hash: string
|
||||
|
|
|
|||
|
|
@ -189,7 +189,6 @@ export default class {
|
|||
this.log("paying external invoice", invoice)
|
||||
const routingFeeLimit = this.lnd.GetFeeLimitAmount(payAmount)
|
||||
await this.storage.userStorage.DecrementUserBalance(userId, totalAmountToDecrement + routingFeeLimit, invoice)
|
||||
console.log("decremented")
|
||||
const pendingPayment = await this.storage.paymentStorage.AddPendingExternalPayment(userId, invoice, payAmount, linkedApplication)
|
||||
try {
|
||||
const payment = await this.lnd.PayInvoice(invoice, amountForLnd, routingFeeLimit)
|
||||
|
|
@ -523,9 +522,8 @@ export default class {
|
|||
}
|
||||
}
|
||||
|
||||
async SendUserToUserPayment(fromUserId: string, toUserId: string, amount: number, linkedApplication: Application): Promise<number> {
|
||||
let sentAmount = 0
|
||||
await this.storage.StartTransaction(async tx => {
|
||||
async SendUserToUserPayment(fromUserId: string, toUserId: string, amount: number, linkedApplication: Application): Promise<{ amount: number, fees: number }> {
|
||||
const payment = await this.storage.StartTransaction(async tx => {
|
||||
const fromUser = await this.storage.userStorage.GetUser(fromUserId, tx)
|
||||
const toUser = await this.storage.userStorage.GetUser(toUserId, tx)
|
||||
if (fromUser.locked || toUser.locked) {
|
||||
|
|
@ -536,21 +534,21 @@ export default class {
|
|||
}
|
||||
const isAppUserPayment = fromUser.user_id !== linkedApplication.owner.user_id
|
||||
let fee = this.getServiceFee(Types.UserOperationType.OUTGOING_USER_TO_USER, amount, isAppUserPayment)
|
||||
const toIncrement = amount - fee
|
||||
const paymentEntry = await this.storage.paymentStorage.CreateUserToUserPayment(fromUserId, toUserId, amount, fee, linkedApplication, tx)
|
||||
await this.storage.userStorage.DecrementUserBalance(fromUser.user_id, amount, `${toUserId}:${paymentEntry.serial_id}`, tx)
|
||||
await this.storage.userStorage.IncrementUserBalance(toUser.user_id, toIncrement, `${fromUserId}:${paymentEntry.serial_id}`, tx)
|
||||
await this.storage.paymentStorage.SaveUserToUserPayment(paymentEntry, tx)
|
||||
const toDecrement = amount + fee
|
||||
const paymentEntry = await this.storage.paymentStorage.AddPendingUserToUserPayment(fromUserId, toUserId, amount, fee, linkedApplication, tx)
|
||||
await this.storage.userStorage.DecrementUserBalance(fromUser.user_id, toDecrement, `${toUserId}:${paymentEntry.serial_id}`, tx)
|
||||
await this.storage.userStorage.IncrementUserBalance(toUser.user_id, amount, `${fromUserId}:${paymentEntry.serial_id}`, tx)
|
||||
await this.storage.paymentStorage.SetPendingUserToUserPaymentAsPaid(paymentEntry.serial_id, tx)
|
||||
if (isAppUserPayment && fee > 0) {
|
||||
await this.storage.userStorage.IncrementUserBalance(linkedApplication.owner.user_id, fee, 'fees', tx)
|
||||
}
|
||||
sentAmount = toIncrement
|
||||
return paymentEntry
|
||||
})
|
||||
const fromUser = await this.storage.userStorage.GetUser(fromUserId)
|
||||
const toUser = await this.storage.userStorage.GetUser(toUserId)
|
||||
this.storage.eventsLog.LogEvent({ type: 'u2u_sender', userId: fromUserId, appId: linkedApplication.app_id, appUserId: "", balance: fromUser.balance_sats, data: toUserId, amount: amount })
|
||||
this.storage.eventsLog.LogEvent({ type: 'u2u_sender', userId: fromUserId, appId: linkedApplication.app_id, appUserId: "", balance: fromUser.balance_sats, data: toUserId, amount: payment.paid_amount + payment.service_fees })
|
||||
this.storage.eventsLog.LogEvent({ type: 'u2u_receiver', userId: toUserId, appId: linkedApplication.app_id, appUserId: "", balance: toUser.balance_sats, data: fromUserId, amount: amount })
|
||||
return sentAmount
|
||||
return { amount: payment.paid_amount, fees: payment.service_fees }
|
||||
}
|
||||
|
||||
async CheckNewlyConfirmedTxs(height: number) {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import Storage from '../storage/index.js'
|
|||
import { LightningHandler } from "../lnd/index.js"
|
||||
import { LoggedEvent } from '../storage/eventsLog.js'
|
||||
import { Invoice, Payment } from '../../../proto/lnd/lightning';
|
||||
import { getLogger } from '../helpers/logger.js';
|
||||
const LN_INVOICE_REGEX = /^(lightning:)?(lnbc|lntb)[0-9a-zA-Z]+$/;
|
||||
const BITCOIN_ADDRESS_REGEX = /^(bitcoin:)?([13][a-km-zA-HJ-NP-Z1-9]{25,34}|bc1[a-zA-HJ-NP-Z0-9]{39,59})$/;
|
||||
type UniqueDecrementReasons = 'ban'
|
||||
|
|
@ -19,6 +20,7 @@ export default class SanityChecker {
|
|||
incrementSources: Record<string, boolean> = {}
|
||||
decrementSources: Record<string, boolean> = {}
|
||||
decrementEvents: Record<string, { userId: string, refund: number, failure: boolean }> = {}
|
||||
log = getLogger({ appName: "SanityChecker" })
|
||||
users: Record<string, { ts: number, updatedBalance: number }> = {}
|
||||
constructor(storage: Storage, lnd: LightningHandler) {
|
||||
this.storage = storage
|
||||
|
|
@ -201,7 +203,6 @@ export default class SanityChecker {
|
|||
throw new Error("payment failled, should not refund routing fees " + invoice)
|
||||
}
|
||||
if (entry.refund !== amt) {
|
||||
console.log(entry.refund, amt)
|
||||
throw new Error("refund amount mismatch for routing fee refund " + invoice)
|
||||
}
|
||||
}
|
||||
|
|
@ -251,9 +252,8 @@ export default class SanityChecker {
|
|||
|
||||
checkUserEntry(e: LoggedEvent, u: { ts: number, updatedBalance: number } | undefined) {
|
||||
const newEntry = { ts: e.timestampMs, updatedBalance: e.balance + e.amount * (e.type === 'balance_decrement' ? -1 : 1) }
|
||||
console.log(e)
|
||||
if (!u) {
|
||||
console.log(e.userId, "balance starts at", e.balance, "sats and moves by", e.amount * (e.type === 'balance_decrement' ? -1 : 1), "sats, resulting in", newEntry.updatedBalance, "sats")
|
||||
this.log(e.userId, "balance starts at", e.balance, "sats and moves by", e.amount * (e.type === 'balance_decrement' ? -1 : 1), "sats, resulting in", newEntry.updatedBalance, "sats")
|
||||
return newEntry
|
||||
}
|
||||
if (e.timestampMs < u.ts) {
|
||||
|
|
@ -262,7 +262,7 @@ export default class SanityChecker {
|
|||
if (e.balance !== u.updatedBalance) {
|
||||
throw new Error("inconsistent balance update got: " + e.balance + " expected " + u.updatedBalance)
|
||||
}
|
||||
console.log(e.userId, "balance updates from", e.balance, "sats and moves by", e.amount * (e.type === 'balance_decrement' ? -1 : 1), "sats, resulting in", newEntry.updatedBalance, "sats")
|
||||
this.log(e.userId, "balance updates from", e.balance, "sats and moves by", e.amount * (e.type === 'balance_decrement' ? -1 : 1), "sats, resulting in", newEntry.updatedBalance, "sats")
|
||||
return newEntry
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { LoadStorageSettingsFromEnv, StorageSettings } from '../storage/index.js'
|
||||
import { LndSettings } from '../lnd/settings.js'
|
||||
import { LndSettings, NodeSettings } from '../lnd/settings.js'
|
||||
import { LoadWatchdogSettingsFromEnv, WatchdogSettings } from './watchdog.js'
|
||||
import { LoadLndSettingsFromEnv } from '../lnd/index.js'
|
||||
import { EnvMustBeInteger, EnvMustBeNonEmptyString } from '../helpers/envParser.js'
|
||||
|
|
@ -44,7 +44,7 @@ export const LoadMainSettingsFromEnv = (): MainSettings => {
|
|||
}
|
||||
}
|
||||
|
||||
export const LoadTestSettingsFromEnv = (): MainSettings => {
|
||||
export const LoadTestSettingsFromEnv = (): MainSettings & { lndSettings: { otherNode: NodeSettings, thirdNode: NodeSettings } } => {
|
||||
const eventLogPath = `logs/eventLogV2Test${Date.now()}.csv`
|
||||
const settings = LoadMainSettingsFromEnv()
|
||||
return {
|
||||
|
|
@ -52,9 +52,16 @@ export const LoadTestSettingsFromEnv = (): MainSettings => {
|
|||
storageSettings: { dbSettings: { ...settings.storageSettings.dbSettings, databaseFile: ":memory:", metricsDatabaseFile: ":memory:" }, eventLogPath },
|
||||
lndSettings: {
|
||||
...settings.lndSettings,
|
||||
otherLndAddr: EnvMustBeNonEmptyString("LND_OTHER_ADDR"),
|
||||
otherLndCertPath: EnvMustBeNonEmptyString("LND_OTHER_CERT_PATH"),
|
||||
otherLndMacaroonPath: EnvMustBeNonEmptyString("LND_OTHER_MACAROON_PATH")
|
||||
otherNode: {
|
||||
lndAddr: EnvMustBeNonEmptyString("LND_OTHER_ADDR"),
|
||||
lndCertPath: EnvMustBeNonEmptyString("LND_OTHER_CERT_PATH"),
|
||||
lndMacaroonPath: EnvMustBeNonEmptyString("LND_OTHER_MACAROON_PATH")
|
||||
},
|
||||
thirdNode: {
|
||||
lndAddr: EnvMustBeNonEmptyString("LND_THIRD_ADDR"),
|
||||
lndCertPath: EnvMustBeNonEmptyString("LND_THIRD_CERT_PATH"),
|
||||
lndMacaroonPath: EnvMustBeNonEmptyString("LND_THIRD_MACAROON_PATH")
|
||||
}
|
||||
},
|
||||
skipSanityCheck: true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,13 +56,13 @@ process.on("message", (message: ChildProcessRequest) => {
|
|||
sendToNostr(message.appId, message.data, message.relays)
|
||||
break
|
||||
default:
|
||||
console.error("unknown nostr request", message)
|
||||
getLogger({ appName: "nostrMiddleware" })("ERROR", "unknown nostr request", message)
|
||||
break
|
||||
}
|
||||
})
|
||||
const initSubprocessHandler = (settings: NostrSettings) => {
|
||||
if (subProcessHandler) {
|
||||
console.error("nostr settings ignored since handler already exists")
|
||||
getLogger({ appName: "nostrMiddleware" })("ERROR", "nostr settings ignored since handler already exists")
|
||||
return
|
||||
}
|
||||
subProcessHandler = new Handler(settings, event => {
|
||||
|
|
@ -74,7 +74,7 @@ const initSubprocessHandler = (settings: NostrSettings) => {
|
|||
}
|
||||
const sendToNostr: NostrSend = (appId, data, relays) => {
|
||||
if (!subProcessHandler) {
|
||||
console.error("nostr was not initialized")
|
||||
getLogger({ appName: "nostrMiddleware" })("ERROR", "nostr was not initialized")
|
||||
return
|
||||
}
|
||||
subProcessHandler.Send(appId, data, relays)
|
||||
|
|
@ -87,9 +87,10 @@ export default class Handler {
|
|||
subs: Sub[] = []
|
||||
apps: Record<string, AppInfo> = {}
|
||||
eventCallback: (event: NostrEvent) => void
|
||||
log = getLogger({ appName: "nostrMiddleware" })
|
||||
constructor(settings: NostrSettings, eventCallback: (event: NostrEvent) => void) {
|
||||
this.settings = settings
|
||||
console.log(
|
||||
this.log(
|
||||
{
|
||||
...settings,
|
||||
apps: settings.apps.map(app => {
|
||||
|
|
@ -151,7 +152,7 @@ export default class Handler {
|
|||
}
|
||||
const eventId = e.id
|
||||
if (handledEvents.includes(eventId)) {
|
||||
console.log("event already handled")
|
||||
this.log("event already handled")
|
||||
return
|
||||
}
|
||||
handledEvents.push(eventId)
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ export default class {
|
|||
return { executedMigrations, executedMetricsMigrations };
|
||||
}
|
||||
|
||||
StartTransaction(exec: TX<void>, description?: string) {
|
||||
StartTransaction<T>(exec: TX<T>, description?: string) {
|
||||
return this.txQueue.PushToQueue({ exec, dbTx: true, description })
|
||||
}
|
||||
}
|
||||
|
|
@ -283,18 +283,19 @@ export default class {
|
|||
return found
|
||||
}
|
||||
|
||||
async CreateUserToUserPayment(fromUserId: string, toUserId: string, amount: number, fee: number, linkedApplication: Application, dbTx: DataSource | EntityManager) {
|
||||
return dbTx.getRepository(UserToUserPayment).create({
|
||||
async AddPendingUserToUserPayment(fromUserId: string, toUserId: string, amount: number, fee: number, linkedApplication: Application, dbTx: DataSource | EntityManager) {
|
||||
const entry = dbTx.getRepository(UserToUserPayment).create({
|
||||
from_user: await this.userStorage.GetUser(fromUserId, dbTx),
|
||||
to_user: await this.userStorage.GetUser(toUserId, dbTx),
|
||||
paid_at_unix: Math.floor(Date.now() / 1000),
|
||||
paid_at_unix: 0,
|
||||
paid_amount: amount,
|
||||
service_fees: fee,
|
||||
linkedApplication
|
||||
})
|
||||
return dbTx.getRepository(UserToUserPayment).save(entry)
|
||||
}
|
||||
async SaveUserToUserPayment(payment: UserToUserPayment, dbTx: DataSource | EntityManager) {
|
||||
return dbTx.getRepository(UserToUserPayment).save(payment)
|
||||
async SetPendingUserToUserPaymentAsPaid(serialId: number, dbTx: DataSource | EntityManager) {
|
||||
dbTx.getRepository(UserToUserPayment).update(serialId, { paid_at_unix: Math.floor(Date.now() / 1000) })
|
||||
}
|
||||
|
||||
GetUserToUserReceivedPayments(userId: string, fromIndex: number, take = 50, entityManager = this.DB) {
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
import { defaultInvoiceExpiry } from '../services/storage/paymentStorage.js'
|
||||
import { Describe, expect, expectThrowsAsync, runSanityCheck, safelySetUserBalance, SetupTest, TestBase } from './testBase.js'
|
||||
export const ignore = false
|
||||
|
||||
export const dev = false
|
||||
export default async (T: TestBase) => {
|
||||
await safelySetUserBalance(T, T.user1, 2000)
|
||||
await testSuccessfulExternalPayment(T)
|
||||
await testFailedExternalPayment(T)
|
||||
await runSanityCheck(T)
|
||||
}
|
||||
|
||||
|
||||
const testSuccessfulExternalPayment = async (T: TestBase) => {
|
||||
T.d("starting testSuccessfulExternalPayment")
|
||||
const application = await T.main.storage.applicationStorage.GetApplication(T.app.appId)
|
||||
const invoice = await T.externalAccessToOtherLnd.NewInvoice(500, "test", defaultInvoiceExpiry)
|
||||
expect(invoice.payRequest).to.startWith("lnbcrt5u")
|
||||
|
|
@ -27,3 +29,19 @@ const testSuccessfulExternalPayment = async (T: TestBase) => {
|
|||
|
||||
}
|
||||
|
||||
const testFailedExternalPayment = async (T: TestBase) => {
|
||||
T.d("starting testFailedExternalPayment")
|
||||
const application = await T.main.storage.applicationStorage.GetApplication(T.app.appId)
|
||||
const invoice = await T.externalAccessToOtherLnd.NewInvoice(1500, "test", defaultInvoiceExpiry)
|
||||
expect(invoice.payRequest).to.startWith("lnbcrt15u")
|
||||
T.d("generated 1500 sats invoice for external node")
|
||||
|
||||
await expectThrowsAsync(T.main.paymentManager.PayInvoice(T.user1.userId, { invoice: invoice.payRequest, amount: 0 }, application), "not enough balance to decrement")
|
||||
T.d("payment failed as expected, with the expected error message")
|
||||
const u1 = await T.main.storage.userStorage.GetUser(T.user1.userId)
|
||||
expect(u1.balance_sats).to.be.equal(1496)
|
||||
T.d("user1 balance is still 1496")
|
||||
const owner = await T.main.storage.userStorage.GetUser(application.owner.user_id)
|
||||
expect(owner.balance_sats).to.be.equal(3)
|
||||
T.d("app balance is still 3 sats")
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ export default async (T: TestBase) => {
|
|||
}
|
||||
|
||||
const testSuccessfulInternalPayment = async (T: TestBase) => {
|
||||
T.d("starting testSuccessfulInternalPayment")
|
||||
const application = await T.main.storage.applicationStorage.GetApplication(T.app.appId)
|
||||
const invoice = await T.main.paymentManager.NewInvoice(T.user2.userId, { amountSats: 1000, memo: "test" }, { linkedApplication: application, expiry: defaultInvoiceExpiry })
|
||||
expect(invoice.invoice).to.startWith("lnbcrt10u")
|
||||
|
|
@ -29,6 +30,7 @@ const testSuccessfulInternalPayment = async (T: TestBase) => {
|
|||
}
|
||||
|
||||
const testFailedInternalPayment = async (T: TestBase) => {
|
||||
T.d("starting testFailedInternalPayment")
|
||||
const application = await T.main.storage.applicationStorage.GetApplication(T.app.appId)
|
||||
const invoice = await T.main.paymentManager.NewInvoice(T.user2.userId, { amountSats: 1000, memo: "test" }, { linkedApplication: application, expiry: defaultInvoiceExpiry })
|
||||
expect(invoice.invoice).to.startWith("lnbcrt10u")
|
||||
|
|
|
|||
|
|
@ -12,18 +12,15 @@ export default async (T: TestBase) => {
|
|||
|
||||
|
||||
const testSpamExternalPayment = async (T: TestBase) => {
|
||||
T.d("starting testSpamExternalPayment")
|
||||
const application = await T.main.storage.applicationStorage.GetApplication(T.app.appId)
|
||||
const invoices = await Promise.all(new Array(10).fill(0).map(() => T.externalAccessToOtherLnd.NewInvoice(500, "test", defaultInvoiceExpiry)))
|
||||
T.d("generated 10 500 sats invoices for external node")
|
||||
const res = await Promise.all(invoices.map(async (invoice, i) => {
|
||||
try {
|
||||
T.d("trying to pay invoice " + i)
|
||||
const result = await T.main.paymentManager.PayInvoice(T.user1.userId, { invoice: invoice.payRequest, amount: 0 }, application)
|
||||
T.d("payment succeeded " + i)
|
||||
return { success: true, result }
|
||||
} catch (e: any) {
|
||||
T.d("payment failed " + i)
|
||||
console.log(e, i)
|
||||
return { success: false, err: e }
|
||||
}
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ export default async (T: TestBase) => {
|
|||
|
||||
|
||||
const testSpamExternalPayment = async (T: TestBase) => {
|
||||
T.d("starting testSpamExternalPayment")
|
||||
const application = await T.main.storage.applicationStorage.GetApplication(T.app.appId)
|
||||
const invoicesForExternal = await Promise.all(new Array(5).fill(0).map(() => T.externalAccessToOtherLnd.NewInvoice(500, "test", defaultInvoiceExpiry)))
|
||||
const invoicesForUser2 = await Promise.all(new Array(5).fill(0).map(() => T.main.paymentManager.NewInvoice(T.user2.userId, { amountSats: 500, memo: "test" }, { linkedApplication: application, expiry: defaultInvoiceExpiry })))
|
||||
|
|
@ -20,13 +21,9 @@ const testSpamExternalPayment = async (T: TestBase) => {
|
|||
T.d("generated 10 500 sats mixed invoices between external node and user 2")
|
||||
const res = await Promise.all(invoices.map(async (invoice, i) => {
|
||||
try {
|
||||
T.d("trying to pay invoice " + i)
|
||||
const result = await T.main.paymentManager.PayInvoice(T.user1.userId, { invoice: invoice, amount: 0 }, application)
|
||||
T.d("payment succeeded " + i)
|
||||
return { success: true, result }
|
||||
} catch (e: any) {
|
||||
T.d("payment failed " + i)
|
||||
console.log(e, i)
|
||||
return { success: false, err: e }
|
||||
}
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ export type TestBase = {
|
|||
user2: TestUserData
|
||||
externalAccessToMainLnd: LND
|
||||
externalAccessToOtherLnd: LND
|
||||
externalAccessToThirdLnd: LND
|
||||
d: Describe
|
||||
}
|
||||
|
||||
|
|
@ -45,16 +46,21 @@ export const SetupTest = async (d: Describe): Promise<TestBase> => {
|
|||
|
||||
|
||||
const externalAccessToMainLnd = new LND(settings.lndSettings, console.log, console.log, () => { }, () => { })
|
||||
const otherLndSetting = { ...settings.lndSettings, lndCertPath: settings.lndSettings.otherLndCertPath, lndMacaroonPath: settings.lndSettings.otherLndMacaroonPath, lndAddr: settings.lndSettings.otherLndAddr }
|
||||
const externalAccessToOtherLnd = new LND(otherLndSetting, console.log, console.log, () => { }, () => { })
|
||||
await externalAccessToMainLnd.Warmup()
|
||||
|
||||
const otherLndSetting = { ...settings.lndSettings, mainNode: settings.lndSettings.otherNode }
|
||||
const externalAccessToOtherLnd = new LND(otherLndSetting, console.log, console.log, () => { }, () => { })
|
||||
await externalAccessToOtherLnd.Warmup()
|
||||
|
||||
const thirdLndSetting = { ...settings.lndSettings, mainNode: settings.lndSettings.thirdNode }
|
||||
const externalAccessToThirdLnd = new LND(thirdLndSetting, console.log, console.log, () => { }, () => { })
|
||||
await externalAccessToThirdLnd.Warmup()
|
||||
|
||||
|
||||
return {
|
||||
expect, main, app,
|
||||
user1, user2,
|
||||
externalAccessToMainLnd, externalAccessToOtherLnd,
|
||||
externalAccessToMainLnd, externalAccessToOtherLnd, externalAccessToThirdLnd,
|
||||
d
|
||||
}
|
||||
}
|
||||
|
|
@ -64,6 +70,7 @@ export const teardown = async (T: TestBase) => {
|
|||
T.main.lnd.Stop()
|
||||
T.externalAccessToMainLnd.Stop()
|
||||
T.externalAccessToOtherLnd.Stop()
|
||||
T.externalAccessToThirdLnd.Stop()
|
||||
console.log("teardown")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,17 +4,38 @@ import { Describe, SetupTest, teardown, TestBase } from './testBase.js'
|
|||
|
||||
type TestModule = {
|
||||
ignore?: boolean
|
||||
dev?: boolean
|
||||
default: (T: TestBase) => Promise<void>
|
||||
}
|
||||
let failures = 0
|
||||
const start = async () => {
|
||||
|
||||
const files = await globby("**/*.spec.js")
|
||||
const modules: { file: string, module: TestModule }[] = []
|
||||
let devModule = -1
|
||||
for (const file of files) {
|
||||
console.log(file)
|
||||
const module = await import(`./${file.slice("build/src/tests/".length)}`) as TestModule
|
||||
modules.push({ module, file })
|
||||
if (module.dev) {
|
||||
console.log("dev module found", file)
|
||||
if (devModule !== -1) {
|
||||
console.error(redConsole, "there are multiple dev modules", resetConsole)
|
||||
return
|
||||
}
|
||||
devModule = modules.length - 1
|
||||
}
|
||||
}
|
||||
if (devModule !== -1) {
|
||||
console.log("running dev module")
|
||||
await runTestFile(modules[devModule].file, modules[devModule].module)
|
||||
return
|
||||
}
|
||||
else {
|
||||
console.log("running all tests")
|
||||
for (const { file, module } of modules) {
|
||||
await runTestFile(file, module)
|
||||
}
|
||||
}
|
||||
if (failures) {
|
||||
console.error(redConsole, "there have been", `${failures}`, "failures in all tests", resetConsole)
|
||||
} else {
|
||||
|
|
@ -24,11 +45,15 @@ const start = async () => {
|
|||
}
|
||||
|
||||
const runTestFile = async (fileName: string, mod: TestModule) => {
|
||||
console.log(fileName)
|
||||
const d = getDescribe(fileName)
|
||||
if (mod.ignore) {
|
||||
d("-----ignoring file-----")
|
||||
d("-----ignoring this file-----")
|
||||
return
|
||||
}
|
||||
if (mod.dev) {
|
||||
d("-----running only this file-----")
|
||||
}
|
||||
const T = await SetupTest(d)
|
||||
try {
|
||||
d("test starting")
|
||||
|
|
@ -39,6 +64,9 @@ const runTestFile = async (fileName: string, mod: TestModule) => {
|
|||
d(e, true)
|
||||
await teardown(T)
|
||||
}
|
||||
if (mod.dev) {
|
||||
d("dev mod is not allowed to in CI, failing for precaution", true)
|
||||
}
|
||||
}
|
||||
|
||||
const getDescribe = (fileName: string): Describe => {
|
||||
|
|
|
|||
35
src/tests/userToUserPayment.spec.ts
Normal file
35
src/tests/userToUserPayment.spec.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import { defaultInvoiceExpiry } from '../services/storage/paymentStorage.js'
|
||||
import { Describe, expect, expectThrowsAsync, runSanityCheck, safelySetUserBalance, SetupTest, TestBase } from './testBase.js'
|
||||
export const ignore = false
|
||||
export const dev = true
|
||||
export default async (T: TestBase) => {
|
||||
await safelySetUserBalance(T, T.user1, 2000)
|
||||
await testSuccessfulU2UPayment(T)
|
||||
await testFailedInternalPayment(T)
|
||||
await runSanityCheck(T)
|
||||
}
|
||||
|
||||
const testSuccessfulU2UPayment = async (T: TestBase) => {
|
||||
T.d("starting testSuccessfulU2UPayment")
|
||||
const application = await T.main.storage.applicationStorage.GetApplication(T.app.appId)
|
||||
const sentAmt = await T.main.paymentManager.SendUserToUserPayment(T.user1.userId, T.user2.userId, 1000, application)
|
||||
expect(sentAmt.amount).to.be.equal(1000)
|
||||
T.d("paid 1000 sats u2u from user1 to user2")
|
||||
const u1 = await T.main.storage.userStorage.GetUser(T.user1.userId)
|
||||
const u2 = await T.main.storage.userStorage.GetUser(T.user2.userId)
|
||||
const owner = await T.main.storage.userStorage.GetUser(application.owner.user_id)
|
||||
expect(u2.balance_sats).to.be.equal(1000)
|
||||
T.d("user2 balance is 1000")
|
||||
expect(u1.balance_sats).to.be.equal(994)
|
||||
T.d("user1 balance is 994 cuz he paid 6 sats fee")
|
||||
expect(owner.balance_sats).to.be.equal(6)
|
||||
T.d("app balance is 6 sats")
|
||||
}
|
||||
|
||||
const testFailedInternalPayment = async (T: TestBase) => {
|
||||
T.d("starting testFailedInternalPayment")
|
||||
const application = await T.main.storage.applicationStorage.GetApplication(T.app.appId)
|
||||
await expectThrowsAsync(T.main.paymentManager.SendUserToUserPayment(T.user1.userId, T.user2.userId, 1000, application), "not enough balance to send payment")
|
||||
T.d("payment failed as expected, with the expected error message")
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue