Merge pull request #667 from shocknet/tests-ci

tests ci
This commit is contained in:
boufni95 2024-04-26 15:44:55 +02:00 committed by GitHub
commit 2f3c19473c
21 changed files with 1417 additions and 48 deletions

55
.github/workflows/test.yaml vendored Normal file
View file

@ -0,0 +1,55 @@
name: Docker Compose Actions Workflow
on: push
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: unzip the file
run: unzip src/tests/regtestNetwork.zip
- name: list files
run: ls -la
- name: Build the stack
run: docker-compose --project-directory ./ -f src/tests/docker-compose.yml up -d
- name: Copy alice cert file
run: docker cp polar-n2-alice:/home/lnd/.lnd/tls.cert alice-tls.cert
- name: Copy bob cert file
run: docker cp polar-n2-bob:/home/lnd/.lnd/tls.cert bob-tls.cert
- name: Copy carol cert file
run: docker cp polar-n2-carol:/home/lnd/.lnd/tls.cert carol-tls.cert
- name: Copy dave cert file
run: docker cp polar-n2-dave:/home/lnd/.lnd/tls.cert dave-tls.cert
- name: Copy alice macaroon file
run: docker cp polar-n2-alice:/home/lnd/.lnd/data/chain/bitcoin/regtest/admin.macaroon alice-admin.macaroon
- name: Copy bob macaroon file
run: docker cp polar-n2-bob:/home/lnd/.lnd/data/chain/bitcoin/regtest/admin.macaroon bob-admin.macaroon
- name: Copy carol macaroon file
run: docker cp polar-n2-carol:/home/lnd/.lnd/data/chain/bitcoin/regtest/admin.macaroon carol-admin.macaroon
- name: Copy dave macaroon file
run: docker cp polar-n2-dave:/home/lnd/.lnd/data/chain/bitcoin/regtest/admin.macaroon dave-admin.macaroon
- name: copy env file
run: cp src/tests/.env.test .env
- name: List files
run: ls -la
- name: Cache node modules
id: cache-npm
uses: actions/cache@v3
env:
cache-name: cache-node-modules
with:
# npm cache files are stored in `~/.npm` on Linux/macOS
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- if: ${{ steps.cache-npm.outputs.cache-hit != 'true' }}
name: List the state of node modules
continue-on-error: true
run: npm list
- name: Install dependencies
run: npm install
- name: Run tests
run: npm test

994
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -35,6 +35,7 @@
"@types/secp256k1": "^4.0.3", "@types/secp256k1": "^4.0.3",
"axios": "^0.28.0", "axios": "^0.28.0",
"bech32": "^2.0.0", "bech32": "^2.0.0",
"bitcoin-core": "^4.2.0",
"chai": "^4.3.7", "chai": "^4.3.7",
"chai-string": "^1.5.0", "chai-string": "^1.5.0",
"copyfiles": "^2.4.1", "copyfiles": "^2.4.1",
@ -77,4 +78,4 @@
"ts-node": "10.7.0", "ts-node": "10.7.0",
"typescript": "4.5.2" "typescript": "4.5.2"
} }
} }

View file

@ -27,7 +27,7 @@ export interface LightningHandler {
PayInvoice(invoice: string, amount: number, feeLimit: number): Promise<PaidInvoice> PayInvoice(invoice: string, amount: number, feeLimit: number): Promise<PaidInvoice>
EstimateChainFees(address: string, amount: number, targetConf: number): Promise<EstimateFeeResponse> EstimateChainFees(address: string, amount: number, targetConf: number): Promise<EstimateFeeResponse>
PayAddress(address: string, amount: number, satPerVByte: number, label?: string): Promise<SendCoinsResponse> PayAddress(address: string, amount: number, satPerVByte: number, label?: string): Promise<SendCoinsResponse>
OpenChannel(destination: string, closeAddress: string, fundingAmount: number, pushSats: number): Promise<string> //OpenChannel(destination: string, closeAddress: string, fundingAmount: number, pushSats: number): Promise<string>
SetMockInvoiceAsPaid(invoice: string, amount: number): Promise<void> SetMockInvoiceAsPaid(invoice: string, amount: number): Promise<void>
ChannelBalance(): Promise<{ local: number, remote: number }> ChannelBalance(): Promise<{ local: number, remote: number }>
GetTransactions(startHeight: number): Promise<TransactionDetails> GetTransactions(startHeight: number): Promise<TransactionDetails>

View file

@ -80,7 +80,22 @@ export default class {
this.SubscribeInvoicePaid() this.SubscribeInvoicePaid()
this.SubscribeNewBlock() this.SubscribeNewBlock()
this.SubscribeHtlcEvents() this.SubscribeHtlcEvents()
this.ready = true const now = Date.now()
return new Promise<void>((res, rej) => {
const interval = setInterval(async () => {
try {
await this.GetInfo()
clearInterval(interval)
this.ready = true
res()
} catch (err) {
this.log("LND is not ready yet, will try again in 1 second")
if (Date.now() - now > 1000 * 60) {
rej(new Error("LND not ready after 1 minute"))
}
}
}, 1000)
})
} }
async GetInfo(): Promise<NodeInfo> { async GetInfo(): Promise<NodeInfo> {
@ -277,6 +292,7 @@ export default class {
stream.responses.onMessage(payment => { stream.responses.onMessage(payment => {
switch (payment.status) { switch (payment.status) {
case Payment_PaymentStatus.FAILED: case Payment_PaymentStatus.FAILED:
console.log(payment)
this.log("invoice payment failed", payment.failureReason) this.log("invoice payment failed", payment.failureReason)
rej(PaymentFailureReason[payment.failureReason]) rej(PaymentFailureReason[payment.failureReason])
return return
@ -356,29 +372,37 @@ export default class {
return res.response return res.response
} }
async OpenChannel(destination: string, closeAddress: string, fundingAmount: number, pushSats: number): Promise<string> { async ConnectPeer(addr: { pubkey: string, host: string }) {
await this.Health() const res = await this.lightning.connectPeer({
addr,
perm: true,
timeout: 0n
}, DeadLineMetadata())
return res.response
}
async ListPeers() {
const res = await this.lightning.listPeers({ latestError: true }, DeadLineMetadata())
return res.response
}
async OpenChannel(destination: string, closeAddress: string, fundingAmount: number, pushSats: number) {
const abortController = new AbortController() const abortController = new AbortController()
const req = OpenChannelReq(destination, closeAddress, fundingAmount, pushSats) const req = OpenChannelReq(destination, closeAddress, fundingAmount, pushSats)
const stream = this.lightning.openChannel(req, { abort: abortController.signal }) const stream = this.lightning.openChannel(req, { abort: abortController.signal })
return new Promise((res, rej) => { return new Promise((res, rej) => {
stream.responses.onMessage(message => { stream.responses.onMessage(message => {
console.log("message", message)
switch (message.update.oneofKind) { switch (message.update.oneofKind) {
case 'chanPending': case 'chanPending':
abortController.abort()
res(Buffer.from(message.pendingChanId).toString('base64')) res(Buffer.from(message.pendingChanId).toString('base64'))
break break
default:
abortController.abort()
rej("unexpected state response: " + message.update.oneofKind)
} }
}) })
stream.responses.onError(error => { stream.responses.onError(error => {
console.log("error", error)
rej(error) rej(error)
}) })
}) })
} }
} }

View file

@ -48,7 +48,7 @@ export default class {
async GetForwardingHistory(indexOffset: number): Promise<{ fee: number, chanIdIn: string, chanIdOut: string, timestampNs: number, offset: number }[]> { throw new Error("GetForwardingHistory disabled in mock mode") } async GetForwardingHistory(indexOffset: number): Promise<{ fee: number, chanIdIn: string, chanIdOut: string, timestampNs: number, offset: number }[]> { throw new Error("GetForwardingHistory disabled in mock mode") }
async GetInfo(): Promise<NodeInfo> { async GetInfo(): Promise<NodeInfo> {
return { alias: "mock", syncedToChain: true, syncedToGraph: true, blockHeight: 1, blockHash: "" } return { alias: "mock", syncedToChain: true, syncedToGraph: true, blockHeight: 1, blockHash: "", identityPubkey: "mock", uris: [] }
} }
async Health(): Promise<void> { } async Health(): Promise<void> { }

View file

@ -1,4 +1,4 @@
import { OpenChannelRequest } from "../../../proto/lnd/lightning"; import { CommitmentType, OpenChannelRequest } from "../../../proto/lnd/lightning.js";
export const OpenChannelReq = (destination: string, closeAddress: string, fundingAmount: number, pushSats: number): OpenChannelRequest => ({ export const OpenChannelReq = (destination: string, closeAddress: string, fundingAmount: number, pushSats: number): OpenChannelRequest => ({
nodePubkey: Buffer.from(destination, 'hex'), nodePubkey: Buffer.from(destination, 'hex'),
@ -9,22 +9,22 @@ export const OpenChannelReq = (destination: string, closeAddress: string, fundin
satPerVbyte: 0n, // TBD satPerVbyte: 0n, // TBD
private: false, private: false,
minConfs: 0, // TBD minConfs: 0, // TBD
baseFee: 0n, // TBD baseFee: 1n, // TBD
feeRate: 0n, // TBD feeRate: 1n, // TBD
targetConf: 0, targetConf: 0,
zeroConf: false, zeroConf: false,
maxLocalCsv: 0, maxLocalCsv: 0,
remoteCsvDelay: 0, remoteCsvDelay: 0,
spendUnconfirmed: false, spendUnconfirmed: false,
minHtlcMsat: 0n, minHtlcMsat: 1n,
remoteChanReserveSat: 0n, remoteChanReserveSat: 10000n,
remoteMaxHtlcs: 0, remoteMaxHtlcs: 483,
remoteMaxValueInFlightMsat: 0n, remoteMaxValueInFlightMsat: 990000000n,
useBaseFee: false, useBaseFee: true,
useFeeRate: false, useFeeRate: true,
// Default stuff // Default stuff
commitmentType: 0, commitmentType: CommitmentType.ANCHORS,
scidAlias: false, scidAlias: false,
nodePubkeyString: "", nodePubkeyString: "",
satPerByte: 0n, satPerByte: 0n,

View file

@ -41,6 +41,8 @@ export type NodeInfo = {
syncedToGraph: boolean syncedToGraph: boolean
blockHeight: number blockHeight: number
blockHash: string blockHash: string
identityPubkey: string
uris: string[]
} }
export type Invoice = { export type Invoice = {
payRequest: string payRequest: string

View file

@ -19,11 +19,12 @@ export default class {
settings: MainSettings settings: MainSettings
paymentManager: PaymentManager paymentManager: PaymentManager
nPubLinkingTokens = new Map<string, NsecLinkingData>(); nPubLinkingTokens = new Map<string, NsecLinkingData>();
linkingTokenInterval: NodeJS.Timeout
constructor(storage: Storage, settings: MainSettings, paymentManager: PaymentManager) { constructor(storage: Storage, settings: MainSettings, paymentManager: PaymentManager) {
this.storage = storage this.storage = storage
this.settings = settings this.settings = settings
this.paymentManager = paymentManager this.paymentManager = paymentManager
setInterval(() => { this.linkingTokenInterval = setInterval(() => {
const now = Date.now(); const now = Date.now();
for (let [token, data] of this.nPubLinkingTokens) { for (let [token, data] of this.nPubLinkingTokens) {
if (data.expiry <= now) { if (data.expiry <= now) {
@ -35,6 +36,9 @@ export default class {
} }
}, 60 * 1000); // 1 minute }, 60 * 1000); // 1 minute
} }
Stop() {
clearInterval(this.linkingTokenInterval)
}
SignAppToken(appId: string): string { SignAppToken(appId: string): string {
return jwt.sign({ appId }, this.settings.jwtSecret); return jwt.sign({ appId }, this.settings.jwtSecret);
} }

View file

@ -50,6 +50,11 @@ export default class {
this.appUserManager = new AppUserManager(this.storage, this.settings, this.applicationManager) this.appUserManager = new AppUserManager(this.storage, this.settings, this.applicationManager)
} }
Stop() {
this.lnd.Stop()
this.applicationManager.Stop()
this.paymentManager.Stop()
}
attachNostrSend(f: NostrSend) { attachNostrSend(f: NostrSend) {
this.nostrSend = f this.nostrSend = f

View file

@ -39,6 +39,7 @@ const defaultLnurlPayMetadata = `[["text/plain", "lnurl pay to Lightning.pub"]]`
const confInOne = 1000 * 1000 const confInOne = 1000 * 1000
const confInTwo = 100 * 1000 * 1000 const confInTwo = 100 * 1000 * 1000
export default class { export default class {
storage: Storage storage: Storage
settings: MainSettings settings: MainSettings
lnd: LightningHandler lnd: LightningHandler
@ -54,6 +55,9 @@ export default class {
this.addressPaidCb = addressPaidCb this.addressPaidCb = addressPaidCb
this.invoicePaidCb = invoicePaidCb this.invoicePaidCb = invoicePaidCb
} }
Stop() {
this.watchDog.Stop()
}
getServiceFee(action: Types.UserOperationType, amount: number, appUser: boolean): number { getServiceFee(action: Types.UserOperationType, amount: number, appUser: boolean): number {
switch (action) { switch (action) {

View file

@ -22,6 +22,12 @@ export type MainSettings = {
skipSanityCheck: boolean skipSanityCheck: boolean
disableExternalPayments: boolean disableExternalPayments: boolean
} }
export type BitcoinCoreSettings = {
port: number
user: string
pass: string
}
export type TestSettings = MainSettings & { lndSettings: { otherNode: NodeSettings, thirdNode: NodeSettings, fourthNode: NodeSettings }, bitcoinCoreSettings: BitcoinCoreSettings }
export const LoadMainSettingsFromEnv = (): MainSettings => { export const LoadMainSettingsFromEnv = (): MainSettings => {
return { return {
watchDogSettings: LoadWatchdogSettingsFromEnv(), watchDogSettings: LoadWatchdogSettingsFromEnv(),
@ -44,7 +50,7 @@ export const LoadMainSettingsFromEnv = (): MainSettings => {
} }
} }
export const LoadTestSettingsFromEnv = (): MainSettings & { lndSettings: { otherNode: NodeSettings, thirdNode: NodeSettings } } => { export const LoadTestSettingsFromEnv = (): TestSettings => {
const eventLogPath = `logs/eventLogV2Test${Date.now()}.csv` const eventLogPath = `logs/eventLogV2Test${Date.now()}.csv`
const settings = LoadMainSettingsFromEnv() const settings = LoadMainSettingsFromEnv()
return { return {
@ -61,8 +67,18 @@ export const LoadTestSettingsFromEnv = (): MainSettings & { lndSettings: { other
lndAddr: EnvMustBeNonEmptyString("LND_THIRD_ADDR"), lndAddr: EnvMustBeNonEmptyString("LND_THIRD_ADDR"),
lndCertPath: EnvMustBeNonEmptyString("LND_THIRD_CERT_PATH"), lndCertPath: EnvMustBeNonEmptyString("LND_THIRD_CERT_PATH"),
lndMacaroonPath: EnvMustBeNonEmptyString("LND_THIRD_MACAROON_PATH") lndMacaroonPath: EnvMustBeNonEmptyString("LND_THIRD_MACAROON_PATH")
},
fourthNode: {
lndAddr: EnvMustBeNonEmptyString("LND_FOURTH_ADDR"),
lndCertPath: EnvMustBeNonEmptyString("LND_FOURTH_CERT_PATH"),
lndMacaroonPath: EnvMustBeNonEmptyString("LND_FOURTH_MACAROON_PATH")
} }
}, },
skipSanityCheck: true skipSanityCheck: true,
bitcoinCoreSettings: {
port: EnvMustBeInteger("BITCOIN_CORE_PORT"),
user: EnvMustBeNonEmptyString("BITCOIN_CORE_USER"),
pass: EnvMustBeNonEmptyString("BITCOIN_CORE_PASS")
}
} }
} }

40
src/tests/.env.test Normal file
View file

@ -0,0 +1,40 @@
LND_ADDRESS=127.0.0.1:10001 #alice
LND_CERT_PATH=alice-tls.cert #alice
LND_MACAROON_PATH=alice-admin.macaroon
DATABASE_FILE=db.sqlite
JWT_SECRET=bigsecrethere
ALLOW_BALANCE_MIGRATION=true
OUTBOUND_MAX_FEE_BPS=60
OUTBOUND_MAX_FEE_EXTRA_SATS=100
INCOMING_CHAIN_FEE_ROOT_BPS=0
OUTGOING_CHAIN_FEE_ROOT_BPS=60 #this is applied only to withdrawls from application wallets
INCOMING_INVOICE_FEE_ROOT_BPS=0
OUTGOING_INVOICE_FEE_ROOT_BPS=60 #this is applied only to withdrawals from application wallets
INCOMING_INVOICE_FEE_USER_BPS=0 #defined by app this is just default
OUTGOING_INVOICE_FEE_USER_BPS=60 #defined by app this is just default
TX_FEE_INTERNAL_ROOT_BPS=60 #this is applied only to withdrawls from application wallets
TX_FEE_INTERNAL_USER_BPS=60 #defined by app this is just default
NOSTR_RELAYS=wss://strfry.shock.network
SERVICE_URL=http://localhost:8080
ADMIN_TOKEN=thisisadmin
PORT=8080
METRICS_DATABASE_FILE=metrics.sqlite
WATCHDOG_MAX_DIFF_BPS=100
WATCHDOG_MAX_DIFF_SATS=10000
# dave <--> alice <--> carol <--> bob
LND_OTHER_ADDR=127.0.0.1:10002
LND_OTHER_CERT_PATH=bob-tls.cert
LND_OTHER_MACAROON_PATH=bob-admin.macaroon
LND_THIRD_ADDR=127.0.0.1:10003
LND_THIRD_CERT_PATH=carol-tls.cert
LND_THIRD_MACAROON_PATH=carol-admin.macaroon
LND_FOURTH_ADDR=127.0.0.1:10004
LND_FOURTH_CERT_PATH=dave-tls.cert
LND_FOURTH_MACAROON_PATH=dave-admin.macaroon
BITCOIN_CORE_PORT=18443
BITCOIN_CORE_USER=polaruser
BITCOIN_CORE_PASS=polarpass

17
src/tests/DockerFile Normal file
View file

@ -0,0 +1,17 @@
FROM node:10-alpine
RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app
WORKDIR /home/node/app
COPY package*.json ./
USER node
RUN npm install
COPY env.example .env
COPY --chown=node:node . .
CMD [ "npm", "test" ]

39
src/tests/bitcoinCore.ts Normal file
View file

@ -0,0 +1,39 @@
// @ts-ignore
import BitcoinCore from 'bitcoin-core';
import { TestSettings } from '../services/main/settings';
export class BitcoinCoreWrapper {
core: BitcoinCore
addr: { address: string }
constructor(settings: TestSettings) {
this.core = new BitcoinCore({
//network: 'regtest',
host: '127.0.0.1',
port: `${settings.bitcoinCoreSettings.port}`,
username: settings.bitcoinCoreSettings.user,
password: settings.bitcoinCoreSettings.pass,
// use a long timeout due to the time it takes to mine a lot of blocks
timeout: 5 * 60 * 1000,
})
}
InitAddress = async () => {
this.addr = await this.core.getNewAddress()
}
Init = async () => {
const wallet = await this.core.createWallet('');
console.log({ wallet })
await this.InitAddress()
console.log({ addr: this.addr })
await this.Mine(101)
const info = await this.core.getWalletInfo();
console.log({ info })
}
Mine = async (blocks: number) => {
await this.core.generateToAddress(blocks, this.addr)
}
SendToAddress = async (address: string, amount: number) => {
const tx = await this.core.sendToAddress(address, amount)
console.log({ tx })
}
}

View file

@ -0,0 +1,108 @@
version: '3.3'
services:
backend1:
environment:
USERID: ${USERID:-1000}
GROUPID: ${GROUPID:-1000}
stop_grace_period: 5m
image: polarlightning/bitcoind:26.0
container_name: polar-n2-backend1
hostname: backend1
command: >-
bitcoind -server=1 -regtest=1 -rpcauth=polaruser:5e5e98c21f5c814568f8b55d83b23c1c$$066b03f92df30b11de8e4b1b1cd5b1b4281aa25205bd57df9be82caf97a05526 -debug=1 -zmqpubrawblock=tcp://0.0.0.0:28334 -zmqpubrawtx=tcp://0.0.0.0:28335 -zmqpubhashblock=tcp://0.0.0.0:28336 -txindex=1 -dnsseed=0 -upnp=0 -rpcbind=0.0.0.0 -rpcallowip=0.0.0.0/0 -rpcport=18443 -rest -listen=1 -listenonion=0 -fallbackfee=0.0002 -blockfilterindex=1 -peerblockfilters=1
volumes:
- ./volumes/bitcoind/backend1:/home/bitcoin/.bitcoin
expose:
- '18443'
- '18444'
- '28334'
- '28335'
ports:
- '18443:18443'
- '19444:18444'
- '28334:28334'
- '29335:28335'
alice:
environment:
USERID: ${USERID:-1000}
GROUPID: ${GROUPID:-1000}
stop_grace_period: 2m
image: polarlightning/lnd:0.17.3-beta
container_name: polar-n2-alice
hostname: alice
command: >-
lnd --noseedbackup --trickledelay=5000 --alias=alice --externalip=alice --tlsextradomain=alice --tlsextradomain=polar-n2-alice --tlsextradomain=host.docker.internal --listen=0.0.0.0:9735 --rpclisten=0.0.0.0:10009 --restlisten=0.0.0.0:8080 --bitcoin.active --bitcoin.regtest --bitcoin.node=bitcoind --bitcoind.rpchost=polar-n2-backend1 --bitcoind.rpcuser=polaruser --bitcoind.rpcpass=polarpass --bitcoind.zmqpubrawblock=tcp://polar-n2-backend1:28334 --bitcoind.zmqpubrawtx=tcp://polar-n2-backend1:28335
restart: always
volumes:
- ./volumes/lnd/alice:/home/lnd/.lnd
expose:
- '8080'
- '10009'
- '9735'
ports:
# - '8081:8080'
- '10001:10009'
- '9735:9735'
bob:
environment:
USERID: ${USERID:-1000}
GROUPID: ${GROUPID:-1000}
stop_grace_period: 2m
image: polarlightning/lnd:0.17.3-beta
container_name: polar-n2-bob
hostname: bob
command: >-
lnd --noseedbackup --trickledelay=5000 --alias=bob --externalip=bob --tlsextradomain=bob --tlsextradomain=polar-n2-bob --tlsextradomain=host.docker.internal --listen=0.0.0.0:9735 --rpclisten=0.0.0.0:10009 --restlisten=0.0.0.0:8080 --bitcoin.active --bitcoin.regtest --bitcoin.node=bitcoind --bitcoind.rpchost=polar-n2-backend1 --bitcoind.rpcuser=polaruser --bitcoind.rpcpass=polarpass --bitcoind.zmqpubrawblock=tcp://polar-n2-backend1:28334 --bitcoind.zmqpubrawtx=tcp://polar-n2-backend1:28335
restart: always
volumes:
- ./volumes/lnd/bob:/home/lnd/.lnd
expose:
- '8080'
- '10009'
- '9735'
ports:
# - '8082:8080'
- '10002:10009'
- '9736:9735'
carol:
environment:
USERID: ${USERID:-1000}
GROUPID: ${GROUPID:-1000}
stop_grace_period: 2m
image: polarlightning/lnd:0.17.3-beta
container_name: polar-n2-carol
hostname: carol
command: >-
lnd --noseedbackup --trickledelay=5000 --alias=carol --externalip=carol --tlsextradomain=carol --tlsextradomain=polar-n2-carol --tlsextradomain=host.docker.internal --listen=0.0.0.0:9735 --rpclisten=0.0.0.0:10009 --restlisten=0.0.0.0:8080 --bitcoin.active --bitcoin.regtest --bitcoin.node=bitcoind --bitcoind.rpchost=polar-n2-backend1 --bitcoind.rpcuser=polaruser --bitcoind.rpcpass=polarpass --bitcoind.zmqpubrawblock=tcp://polar-n2-backend1:28334 --bitcoind.zmqpubrawtx=tcp://polar-n2-backend1:28335
restart: always
volumes:
- ./volumes/lnd/carol:/home/lnd/.lnd
expose:
- '8080'
- '10009'
- '9735'
ports:
# - '8083:8080'
- '10003:10009'
- '9737:9735'
dave:
environment:
USERID: ${USERID:-1000}
GROUPID: ${GROUPID:-1000}
stop_grace_period: 2m
image: polarlightning/lnd:0.17.3-beta
container_name: polar-n2-dave
hostname: dave
command: >-
lnd --noseedbackup --trickledelay=5000 --alias=dave --externalip=dave --tlsextradomain=dave --tlsextradomain=polar-n2-dave --tlsextradomain=host.docker.internal --listen=0.0.0.0:9735 --rpclisten=0.0.0.0:10009 --restlisten=0.0.0.0:8080 --bitcoin.active --bitcoin.regtest --bitcoin.node=bitcoind --bitcoind.rpchost=polar-n2-backend1 --bitcoind.rpcuser=polaruser --bitcoind.rpcpass=polarpass --bitcoind.zmqpubrawblock=tcp://polar-n2-backend1:28334 --bitcoind.zmqpubrawtx=tcp://polar-n2-backend1:28335
restart: always
volumes:
- ./volumes/lnd/dave:/home/lnd/.lnd
expose:
- '8080'
- '10009'
- '9735'
ports:
# - '8084:8080'
- '10004:10009'
- '9738:9735'

61
src/tests/networkSetup.ts Normal file
View file

@ -0,0 +1,61 @@
import { LoadTestSettingsFromEnv } from "../services/main/settings.js"
import { BitcoinCoreWrapper } from "./bitcoinCore.js"
import LND from '../services/lnd/lnd.js'
export const setupNetwork = async () => {
const settings = LoadTestSettingsFromEnv()
const core = new BitcoinCoreWrapper(settings)
await core.InitAddress()
await core.Mine(1)
const alice = new LND(settings.lndSettings, () => { }, () => { }, () => { }, () => { })
const bob = new LND({ ...settings.lndSettings, mainNode: settings.lndSettings.otherNode }, () => { }, () => { }, () => { }, () => { })
await tryUntil<void>(async i => {
const peers = await alice.ListPeers()
if (peers.peers.length > 0) {
return
}
await alice.ConnectPeer({ pubkey: '0232842d81b2423df97aa8a264f8c0811610a736af65afe2e145279f285625c1e4', host: "carol:9735" })
await alice.ConnectPeer({ pubkey: '027c50fde118af534ff27e59da722422d2f3e06505c31e94c1b40c112c48a83b1c', host: "dave:9735" })
}, 15, 2000)
await tryUntil<void>(async i => {
const peers = await bob.ListPeers()
if (peers.peers.length > 0) {
return
}
await bob.ConnectPeer({ pubkey: '0232842d81b2423df97aa8a264f8c0811610a736af65afe2e145279f285625c1e4', host: "carol:9735" })
}, 15, 2000)
await tryUntil<void>(async i => {
const info = await alice.GetInfo()
if (!info.syncedToChain) {
throw new Error("alice not synced to chain")
}
if (!info.syncedToGraph) {
//await lnd.ConnectPeer({})
throw new Error("alice not synced to graph")
}
}, 15, 2000)
await tryUntil<void>(async i => {
const info = await bob.GetInfo()
if (!info.syncedToChain) {
throw new Error("bob not synced to chain")
}
if (!info.syncedToGraph) {
//await lnd.ConnectPeer({})
throw new Error("bob not synced to graph")
}
}, 15, 2000)
}
const tryUntil = async <T>(fn: (attempt: number) => Promise<T>, maxTries: number, interval: number) => {
for (let i = 0; i < maxTries; i++) {
try {
return await fn(i)
} catch (e) {
console.log("tryUntil error", e)
await new Promise(resolve => setTimeout(resolve, interval))
}
}
throw new Error("tryUntil failed")
}

Binary file not shown.

View file

@ -66,8 +66,7 @@ export const SetupTest = async (d: Describe): Promise<TestBase> => {
} }
export const teardown = async (T: TestBase) => { export const teardown = async (T: TestBase) => {
T.main.paymentManager.watchDog.Stop() T.main.Stop()
T.main.lnd.Stop()
T.externalAccessToMainLnd.Stop() T.externalAccessToMainLnd.Stop()
T.externalAccessToOtherLnd.Stop() T.externalAccessToOtherLnd.Stop()
T.externalAccessToThirdLnd.Stop() T.externalAccessToThirdLnd.Stop()

View file

@ -1,16 +1,27 @@
import { globby } from 'globby' import { globby } from 'globby'
import { setupNetwork } from './networkSetup.js'
import { Describe, SetupTest, teardown, TestBase } from './testBase.js' import { Describe, SetupTest, teardown, TestBase } from './testBase.js'
type TestModule = { type TestModule = {
ignore?: boolean ignore?: boolean
dev?: boolean dev?: boolean
default: (T: TestBase) => Promise<void> default: (T: TestBase) => Promise<void>
} }
let failures = 0 let failures = 0
const start = async () => { const getDescribe = (fileName: string): Describe => {
return (message, failure) => {
if (failure) {
failures++
console.error(redConsole, fileName, ": FAILURE ", message, resetConsole)
} else {
console.log(greenConsole, fileName, ":", message, resetConsole)
}
}
}
const files = await globby("**/*.spec.js") const start = async () => {
await setupNetwork()
const files = await globby(["**/*.spec.js", "!**/node_modules/**"])
const modules: { file: string, module: TestModule }[] = [] const modules: { file: string, module: TestModule }[] = []
let devModule = -1 let devModule = -1
for (const file of files) { for (const file of files) {
@ -19,8 +30,7 @@ const start = async () => {
if (module.dev) { if (module.dev) {
console.log("dev module found", file) console.log("dev module found", file)
if (devModule !== -1) { if (devModule !== -1) {
console.error(redConsole, "there are multiple dev modules", resetConsole) throw new Error("there are multiple dev modules")
return
} }
devModule = modules.length - 1 devModule = modules.length - 1
} }
@ -28,16 +38,15 @@ const start = async () => {
if (devModule !== -1) { if (devModule !== -1) {
console.log("running dev module") console.log("running dev module")
await runTestFile(modules[devModule].file, modules[devModule].module) await runTestFile(modules[devModule].file, modules[devModule].module)
return } else {
}
else {
console.log("running all tests") console.log("running all tests")
for (const { file, module } of modules) { for (const { file, module } of modules) {
await runTestFile(file, module) await runTestFile(file, module)
} }
} }
console.log(failures)
if (failures) { if (failures) {
console.error(redConsole, "there have been", `${failures}`, "failures in all tests", resetConsole) throw new Error("there have been " + failures + " failures in all tests")
} else { } else {
console.log(greenConsole, "there have been 0 failures in all tests", resetConsole) console.log(greenConsole, "there have been 0 failures in all tests", resetConsole)
} }
@ -69,16 +78,7 @@ const runTestFile = async (fileName: string, mod: TestModule) => {
} }
} }
const getDescribe = (fileName: string): Describe => {
return (message, failure) => {
if (failure) {
failures++
console.error(redConsole, fileName, ": FAILURE ", message, resetConsole)
} else {
console.log(greenConsole, fileName, ":", message, resetConsole)
}
}
}
const greenConsole = "\x1b[32m" const greenConsole = "\x1b[32m"
const redConsole = "\x1b[31m" const redConsole = "\x1b[31m"
const resetConsole = "\x1b[0m" const resetConsole = "\x1b[0m"

View file

@ -1,7 +1,7 @@
import { defaultInvoiceExpiry } from '../services/storage/paymentStorage.js' import { defaultInvoiceExpiry } from '../services/storage/paymentStorage.js'
import { Describe, expect, expectThrowsAsync, runSanityCheck, safelySetUserBalance, SetupTest, TestBase } from './testBase.js' import { Describe, expect, expectThrowsAsync, runSanityCheck, safelySetUserBalance, SetupTest, TestBase } from './testBase.js'
export const ignore = false export const ignore = false
export const dev = true export const dev = false
export default async (T: TestBase) => { export default async (T: TestBase) => {
await safelySetUserBalance(T, T.user1, 2000) await safelySetUserBalance(T, T.user1, 2000)
await testSuccessfulU2UPayment(T) await testSuccessfulU2UPayment(T)