Skip to content
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ jobs:
echo "Contents of .env file:"
cat .env

- name: Create data directory for SQLite
run: mkdir -p apps/backend/data

- name: TypeScript type check (backend only)
run: pnpm --filter backend run type-check

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,8 @@ export type EntryRepositoryInterface = {
userId: UUID,
transactionId: UUID,
): Promise<EntryDbRow[]>;
deleteByTransactionId(
userId: UUID,
transactionId: UUID,
): Promise<EntryDbRow[]>;
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export type OperationRepositoryInterface = {
userId: UUID,
entryIds: UUID[],
): Promise<OperationDbRow[]>;
deleteByEntryIds(userId: UUID, entryIds: UUID[]): Promise<void>;
};
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,15 @@ export class UpdateTransactionUseCase {
// For now, we will just delete all existing entries and create new ones
// let's think about a better approach later
// If entries are undefined, treat as 'no update to entries'
// we should compare with existing entries and update accordingly
if (data.entries === undefined) {
return this.transactionMapper.toResponseDTO(
transaction,
transactionDbRow.entries,
);
}

await this.softDeleteEntriesByTransactionId(
await this.deleteEntriesByTransactionId(
user.getId().valueOf(),
transactionId,
);
Expand All @@ -82,22 +83,21 @@ export class UpdateTransactionUseCase {
});
}

private async softDeleteEntriesByTransactionId(
private async deleteEntriesByTransactionId(
userId: UUID,
transactionId: UUID,
): Promise<void> {
const softDeletedEntries =
await this.entryRepository.softDeleteByTransactionId(
userId,
transactionId,
);
const deletedEntries = await this.entryRepository.deleteByTransactionId(
userId,
transactionId,
);

const entryIds = softDeletedEntries.map((entry) => entry.id);
const entryIds = deletedEntries.map((entry) => entry.id);

if (entryIds.length === 0) {
return;
}

await this.operationRepository.softDeleteByEntryIds(userId, entryIds);
await this.operationRepository.deleteByEntryIds(userId, entryIds);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ describe('UpdateTransactionUseCase', () => {
};

const mockEntryRepository = {
softDeleteByTransactionId: vi.fn(),
deleteByTransactionId: vi.fn(),
};

const mockOperationRepository = {
softDeleteByEntryIds: vi.fn(),
deleteByEntryIds: vi.fn(),
};

const mockEnsureEntityExistsAndOwned = vi.fn();
Expand Down Expand Up @@ -264,7 +264,7 @@ describe('UpdateTransactionUseCase', () => {

const softDeletedEntries = entries.map((e) => e.toPersistence());

mockEntryRepository.softDeleteByTransactionId.mockResolvedValue(
mockEntryRepository.deleteByTransactionId.mockResolvedValue(
softDeletedEntries,
);

Expand All @@ -289,16 +289,14 @@ describe('UpdateTransactionUseCase', () => {
}),
);

expect(mockEntryRepository.softDeleteByTransactionId).toHaveBeenCalledWith(
expect(mockEntryRepository.deleteByTransactionId).toHaveBeenCalledWith(
user.getId().valueOf(),
transactionDBRow.id,
);

expect(mockOperationRepository.softDeleteByEntryIds).toHaveBeenCalledTimes(
1,
);
expect(mockOperationRepository.deleteByEntryIds).toHaveBeenCalledTimes(1);

expect(mockOperationRepository.softDeleteByEntryIds).toHaveBeenCalledWith(
expect(mockOperationRepository.deleteByEntryIds).toHaveBeenCalledWith(
user.getId().valueOf(),
softDeletedEntries.map((e) => e.id),
);
Expand Down
22 changes: 6 additions & 16 deletions apps/backend/src/db/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,17 @@ import * as schemas from './schemas';

dotenv.config();

const isTestEnvironment =
process.env.NODE_ENV === 'test' || process.env.VITEST === 'true';

export type DataBase = ReturnType<typeof drizzle<typeof schemas>>;

export type TxType = Parameters<Parameters<DataBase['transaction']>[0]>[0];

let db: DataBase;

if (isTestEnvironment) {
const client = createClient({ url: 'file::memory:' });
db = drizzle(client, { schema: schemas });
} else {
const dbUrl = config.dbUrl;
const dbUrl = config.dbUrl;

if (!dbUrl) {
throw new Error('Database URL is not defined');
}

const client = createClient({ url: dbUrl });
db = drizzle(client, { schema: schemas });
if (!dbUrl) {
throw new Error('Database URL is not defined');
}

const client = createClient({ url: dbUrl });
const db = drizzle(client, { schema: schemas });

export { db };
2 changes: 1 addition & 1 deletion apps/backend/src/db/schemas/transactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export type TransactionDbInsert = InferInsertModel<typeof transactionsTable>;
export type TransactionRepoInsert = TransactionDbInsert;

export type TransactionDbUpdate = Partial<
Omit<TransactionDbRow, 'id' | 'userId' | 'createdAt' | 'updatedAt'>
Omit<TransactionDbRow, 'id' | 'userId' | 'createdAt'>
>;

// Type for transaction with nested relations (operations as array)
Expand Down
Loading