Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(core): remove legacy "02" tests #3677

Merged
merged 12 commits into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions bats/core/api/account.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
load "../../helpers/user.bash"

setup_file() {
create_user_with_metadata 'alice'
create_user_with_metadata 'bob'
}

@test "account: sets username" {
vindard marked this conversation as resolved.
Show resolved Hide resolved
suffix="$RANDOM"
username_to_set=$"alice_$suffix"
username_to_set_upper=$"Alice_$suffix"

# Sets a username
local variables=$(
jq -n \
--arg username "$username_to_set" \
'{input: {username: $username}}'
)
exec_graphql 'alice' 'user-update-username' "$variables"
num_errors="$(graphql_output '.data.userUpdateUsername.errors | length')"
[[ "$num_errors" == "0" ]] || exit 1
}
34 changes: 34 additions & 0 deletions core/api/test/integration/app/accounts/set-username.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { setUsername } from "@/app/accounts"
import { UsernameIsImmutableError, UsernameNotAvailableError } from "@/domain/accounts"
import * as MongooseImpl from "@/services/mongoose"

afterEach(() => {
jest.restoreAllMocks()
})

describe("Set username", () => {
it("fails to set the same name on a different user", async () => {
const { AccountsRepository: AccountsRepositoryOrig } =
jest.requireActual("@/services/mongoose")
jest.spyOn(MongooseImpl, "AccountsRepository").mockReturnValue({
...AccountsRepositoryOrig(),
findById: () => true,
findByUsername: () => true,
})

const res = await setUsername({ accountId: crypto.randomUUID(), username: "alice" })
expect(res).toBeInstanceOf(UsernameNotAvailableError)
})

it("fails to change username", async () => {
const { AccountsRepository: AccountsRepositoryOrig } =
jest.requireActual("@/services/mongoose")
jest.spyOn(MongooseImpl, "AccountsRepository").mockReturnValue({
...AccountsRepositoryOrig(),
findById: () => ({ username: "alice" }),
})

const res = await setUsername({ accountId: crypto.randomUUID(), username: "alice" })
expect(res).toBeInstanceOf(UsernameIsImmutableError)
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { randomUUID } from "crypto"

import { Accounts } from "@/app"

import { AccountsRepository } from "@/services/mongoose"
import { createRandomUserAndBtcWallet } from "test/helpers"

describe("updateAccountStatus", () => {
it("sets account status (with history) for given user id", async () => {
const newWalletDescriptor = await createRandomUserAndBtcWallet()
const newAccount = await AccountsRepository().findById(newWalletDescriptor.accountId)
if (newAccount instanceof Error) throw newAccount

const updatedByPrivilegedClientId = randomUUID() as PrivilegedClientId

let account = await Accounts.updateAccountStatus({
accountId: newAccount.id,
status: "pending",
dolcalmi marked this conversation as resolved.
Show resolved Hide resolved
updatedByPrivilegedClientId,
})
if (account instanceof Error) {
throw account
}
expect(account.status).toEqual("pending")

account = await Accounts.updateAccountStatus({
accountId: newAccount.id,
status: "locked",
updatedByPrivilegedClientId,
comment: "Looks spammy",
})
if (account instanceof Error) {
throw account
}
expect(account.statusHistory.slice(-1)[0]).toMatchObject({
status: "locked",
updatedByPrivilegedClientId,
comment: "Looks spammy",
})
expect(account.status).toEqual("locked")

account = await Accounts.updateAccountStatus({
accountId: newAccount.id,
status: "active",
updatedByPrivilegedClientId,
})
if (account instanceof Error) {
throw account
}
expect(account.statusHistory.length).toBe(4)
expect(account.status).toEqual("active")
})
})
100 changes: 100 additions & 0 deletions core/api/test/integration/app/lightning/delete-ln-payments.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { Lightning } from "@/app"

import { TWO_MONTHS_IN_MS } from "@/config"

import { decodeInvoice } from "@/domain/bitcoin/lightning"
import { CouldNotFindLnPaymentFromHashError } from "@/domain/errors"

import * as LndImpl from "@/services/lnd"
import { LnPayment } from "@/services/lnd/schema"
import { LnPaymentsRepository } from "@/services/mongoose"

const DEFAULT_PUBKEY =
"03ca1907342d5d37744cb7038375e1867c24a87564c293157c95b2a9d38dcfb4c2" as Pubkey

const randomRequest =
"lnbcrt10n1p39jatkpp5djwv295kunhe5e0e4whj3dcjzwy7cmcxk8cl2a4dquyrp3dqydesdqqcqzpuxqr23ssp56u5m680x7resnvcelmsngc64ljm7g5q9r26zw0qyq5fenuqlcfzq9qyyssqxv4kvltas2qshhmqnjctnqkjpdfzu89e428ga6yk9jsp8rf382f3t03ex4e6x3a4sxkl7ruj6lsfpkuu9u9ee5kgr5zdyj7x2nwdljgq74025p"
const invoice = decodeInvoice(randomRequest)
if (invoice instanceof Error) throw invoice

afterEach(async () => {
await LnPayment.deleteMany({})

jest.restoreAllMocks()
})

describe("deleteLnPaymentsBefore", () => {
const lnPayments = LnPaymentsRepository()
const { paymentHash, paymentRequest } = invoice
const mockedLnPaymentLookup = {
createdAt: new Date(Date.now()),
paymentHash,
paymentRequest,
}

it("persists payment to be deleted in ln-payments collection", async () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not a test

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you mean? There's a code branch in checkAndDeletePaymentForHash that persists an lnPayment if it didn't previously exist in LnPaymentsRepository that I explicitly wanted to test

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I review bottom -> up, there is no need to have 2 tests for this, is just add

const lnPaymentAfter = await lnPayments.findByPaymentHash(paymentHash)
    if (lnPaymentAfter instanceof Error) throw lnPaymentAfter
    expect(lnPaymentAfter.paymentHash).toBe(paymentHash)

in second test but it is up to you

Copy link
Contributor Author

@vindard vindard Dec 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to have them separate because the 2nd test needs to have an explicit step that marks isCompleteRecord as true and I bring that in manually, and if I tried to not do that then the test becomes more complicated because later tests would rely on prior state in the test

// Setup mocks
const { LndService: LnServiceOrig } = jest.requireActual("@/services/lnd")
jest.spyOn(LndImpl, "LndService").mockReturnValue({
...LnServiceOrig(),
listActivePubkeys: () => [DEFAULT_PUBKEY],
listSettledPayments: () => ({
lnPayments: [mockedLnPaymentLookup],
endCursor: false,
}),
lookupPayment: () => mockedLnPaymentLookup,
})

// Check doesn't exist
const lnPaymentBefore = await lnPayments.findByPaymentHash(paymentHash)
expect(lnPaymentBefore).toBeInstanceOf(CouldNotFindLnPaymentFromHashError)

// Run deleteLnPaymentsBefore
const timestampNow = new Date()
await Lightning.deleteLnPaymentsBefore(timestampNow)

// Check exists
const lnPaymentAfter = await lnPayments.findByPaymentHash(paymentHash)
if (lnPaymentAfter instanceof Error) throw lnPaymentAfter
expect(lnPaymentAfter.paymentHash).toBe(paymentHash)
})

it("deletes ln-payment before appropriate time", async () => {
// Setup mocks
const deletePaymentByHash = jest.fn()
const { LndService: LnServiceOrig } = jest.requireActual("@/services/lnd")
jest.spyOn(LndImpl, "LndService").mockReturnValue({
...LnServiceOrig(),
listActivePubkeys: () => [DEFAULT_PUBKEY],
listSettledPayments: () => ({
lnPayments: [mockedLnPaymentLookup],
endCursor: false,
}),
lookupPayment: () => mockedLnPaymentLookup,
deletePaymentByHash,
})

await lnPayments.persistNew({
paymentRequest,
paymentHash,
sentFromPubkey: DEFAULT_PUBKEY,
})
await lnPayments.update({
paymentHash,
isCompleteRecord: true,
} as PersistedLnPaymentLookup)
Comment on lines +77 to +85
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is what needs to be tested... deleteLnPaymentsBefore should persist it in lnpayment if it does not exists

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I don't understand 😅 ... isn't that what this tests

Copy link
Collaborator

@dolcalmi dolcalmi Dec 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this not done by await Lightning.deleteLnPaymentsBefore(timestamp2Months) line 89?

Copy link
Contributor Author

@vindard vindard Dec 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah nope, it persists it but then it won't get isCompleteRecord property marked as true unless we run updateLnPayments which is a lot more than we need just to test that deletePaymentByHash gets hit under the right conditions

So I just manually setup the lnPayment in the repository so that the use-case can continue on to what we want to test here


// Run deleteLnPaymentsBefore for 2 month old invoices
const timestamp2Months = new Date(Date.now() - TWO_MONTHS_IN_MS)
await Lightning.deleteLnPaymentsBefore(timestamp2Months)
expect(deletePaymentByHash.mock.calls).toHaveLength(0)

// Run deleteLnPaymentsBefore for all invoices
const timestampNow = new Date()
await Lightning.deleteLnPaymentsBefore(timestampNow)
expect(deletePaymentByHash.mock.calls[0][0]).toStrictEqual({
paymentHash,
pubkey: DEFAULT_PUBKEY,
})
})
})
60 changes: 60 additions & 0 deletions core/api/test/integration/app/lightning/update-ln-payments.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Lightning } from "@/app"

import { decodeInvoice } from "@/domain/bitcoin/lightning"
import * as LndImpl from "@/services/lnd"
import { LnPayment } from "@/services/lnd/schema"
import { LnPaymentsRepository } from "@/services/mongoose"

const DEFAULT_PUBKEY =
"03ca1907342d5d37744cb7038375e1867c24a87564c293157c95b2a9d38dcfb4c2" as Pubkey

const randomRequest =
"lnbcrt10n1p39jatkpp5djwv295kunhe5e0e4whj3dcjzwy7cmcxk8cl2a4dquyrp3dqydesdqqcqzpuxqr23ssp56u5m680x7resnvcelmsngc64ljm7g5q9r26zw0qyq5fenuqlcfzq9qyyssqxv4kvltas2qshhmqnjctnqkjpdfzu89e428ga6yk9jsp8rf382f3t03ex4e6x3a4sxkl7ruj6lsfpkuu9u9ee5kgr5zdyj7x2nwdljgq74025p"
const invoice = decodeInvoice(randomRequest)
if (invoice instanceof Error) throw invoice

afterEach(async () => {
await LnPayment.deleteMany({})

jest.restoreAllMocks()
})

describe("updateLnPayments", () => {
const lnPayments = LnPaymentsRepository()
const { paymentHash, paymentRequest } = invoice
const mockedLnPaymentLookup = {
createdAt: new Date(Date.now()),
paymentHash,
paymentRequest,
}

it("updates an incomplete record", async () => {
const { LndService: LnServiceOrig } = jest.requireActual("@/services/lnd")
jest.spyOn(LndImpl, "LndService").mockReturnValue({
...LnServiceOrig(),
listSettledPayments: () => ({
lnPayments: [mockedLnPaymentLookup],
endCursor: false,
}),
})

await lnPayments.persistNew({
paymentRequest,
paymentHash,
sentFromPubkey: DEFAULT_PUBKEY,
})

// Check initial status
const lnPaymentBefore = await lnPayments.findByPaymentHash(paymentHash)
if (lnPaymentBefore instanceof Error) throw lnPaymentBefore
expect(lnPaymentBefore.isCompleteRecord).toBe(false)

// Run update
await Lightning.updateLnPayments()

// Check final status
const lnPaymentAfter = await lnPayments.findByPaymentHash(paymentHash)
if (lnPaymentAfter instanceof Error) throw lnPaymentAfter
expect(lnPaymentAfter.isCompleteRecord).toBe(true)
})
})
67 changes: 67 additions & 0 deletions core/api/test/integration/services/accounts-repository.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { checkedToUsername } from "@/domain/accounts"
import { AccountsRepository } from "@/services/mongoose"
import { createRandomUserAndBtcWallet } from "test/helpers"

const randomUsername = () => {
const num = Math.floor(Math.random() * 10 ** 4)
const username = checkedToUsername(`alice${num}`)
if (username instanceof Error) throw username
return username
}

const accounts = AccountsRepository()
describe("AccountsRepository", () => {
describe("findByUsername", () => {
it("returns valid account for username", async () => {
const username = randomUsername()

// Create user
const newWalletDescriptor = await createRandomUserAndBtcWallet()
const newAccount = await AccountsRepository().findById(
newWalletDescriptor.accountId,
)
if (newAccount instanceof Error) throw newAccount

// Set username
newAccount.username = username
const res = await accounts.update(newAccount)
expect(res).not.toBeInstanceOf(Error)

// Check that username was set
const account = await accounts.findByUsername(username)
if (account instanceof Error) throw account
expect(account.id).toStrictEqual(newAccount.id)
})

it("returns valid account for other capitalization", async () => {
const username = randomUsername()

// Create user
const newWalletDescriptor = await createRandomUserAndBtcWallet()
const newAccount = await AccountsRepository().findById(
newWalletDescriptor.accountId,
)
if (newAccount instanceof Error) throw newAccount

// Set username
newAccount.username = username
const res = await accounts.update(newAccount)
expect(res).not.toBeInstanceOf(Error)

// Check that username query is case insensitive
const account = await accounts.findByUsername(
username.toLocaleUpperCase() as Username,
)
if (account instanceof Error) throw account
expect(account.id).toStrictEqual(newAccount.id)
})

it("errors if username does not exist", async () => {
const username = checkedToUsername("non_user")
if (username instanceof Error) throw username

const account = await accounts.findByUsername(username)
expect(account).toBeInstanceOf(Error)
})
})
})
19 changes: 19 additions & 0 deletions core/api/test/integration/services/ledger-csv.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { CsvWalletsExport } from "@/services/ledger/csv-wallet-export"

import { createRandomUserAndBtcWallet } from "test/helpers"

describe("CsvWalletsExport", () => {
const csvHeader =
"id,walletId,type,credit,debit,fee,currency,timestamp,pendingConfirmation,journalId,lnMemo,usd,feeUsd,recipientWalletId,username,memoFromPayer,paymentHash,pubkey,feeKnownInAdvance,address,txHash"

it("exports to csv", async () => {
const newWalletDescriptor = await createRandomUserAndBtcWallet()

const csv = new CsvWalletsExport()
await csv.addWallet(newWalletDescriptor.id)
const base64Data = csv.getBase64()
expect(typeof base64Data).toBe("string")
const data = Buffer.from(base64Data, "base64")
expect(data.includes(csvHeader)).toBeTruthy()
})
})
Loading
Loading