Skip to content

test(wallets): add integration test for empty trade history - #657

Merged
Chucks1093 merged 1 commit into
accesslayerorg:mainfrom
Fury03:test/642-empty-trade-history
Jul 26, 2026
Merged

test(wallets): add integration test for empty trade history#657
Chucks1093 merged 1 commit into
accesslayerorg:mainfrom
Fury03:test/642-empty-trade-history

Conversation

@Fury03

@Fury03 Fury03 commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #642

Adds src/modules/wallets/wallet-activity-empty-history.integration.test.ts, covering the trade history endpoint (GET /api/v1/wallets/:address/activity) for a wallet that has never traded.

Requests go through the real Express app via supertest, so routing, address validation, the controller, the service and JSON serialization all participate. Only Prisma is mocked — no database required, consistent with the other integration tests in this module.

Acceptance criteria

Criterion Assertion
Response status is 200 expect(response.status).toBe(200)
items is an empty array expect(body.data.items).toEqual([])
has_more is false expect(body.data.meta.hasMore).toBe(false)
next_cursor is null expect(body.data.meta).toHaveProperty('nextCursor', null)

One note on field names

The issue describes the fields as has_more and next_cursor. The endpoint actually serializes them in camelCase (hasMore, nextCursor), nested under data.meta:

{ "success": true, "data": { "items": [], "meta": { "limit": 20, "offset": 0, "total": 0, "hasMore": false, "nextCursor": null } } }

There is no snake_case transform anywhere in the response path, so I've pinned the shape the endpoint really returns rather than the shape the issue names. Flagging it in case the intent was that the API should be snake_case — that would be an API change rather than a test, so it isn't in this PR.

Beyond the four assertions

  • nextCursor is checked with toHaveProperty(..., null), not equality against null. A key that is absent reads back as undefined, and a client doing meta.nextCursor === null to decide whether to stop paging behaves differently from one checking for a falsy value. The criterion is that the key is present and null, so that is what's asserted — plus a check on the raw response text for "nextCursor":null, which pins the wire format directly.
  • The query is asserted to be scoped to the requested wallet. Without this, every other assertion would still pass if the handler queried some other wallet, or none at all — the empty page would be incidental rather than a real answer about this address.
  • The empty-page contract is checked across filters (type=buy, type=sell, creator_id, explicit pagination, offset past the end, combined filters). A filter that happens to match nothing is the same situation as a wallet that never traded, and is the case this endpoint is most likely to hit in production.
  • A cursor case. Cursor paging takes a different branch in fetchWalletActivity than offset paging and computes hasMore from the returned row count rather than the total, so it needs its own coverage.

11 tests.

Mutation-tested

I confirmed the tests are load-bearing rather than green by accident, by breaking the controller three ways and checking each is caught:

Mutation Result
nextCursor omitted from the response 9 of 11 fail
404 returned when items is empty 11 of 11 fail
hasMore hard-coded to true 8 of 11 fail

The controller was restored afterwards — this PR contains no source changes.

One relocation

wallet-activity-invalid-address.integration.test.ts already contained a partial version of this case ('should return 200 with empty data array for a valid Stellar address with no trade history'). It asserted status, items and hasMore but not nextCursor, and it sat in a file whose describe block is Malformed Stellar Address.

Rather than leave a weaker duplicate behind, I removed it there and covered it fully in the new file, which matches how this module already splits by concern (-date-filter, -creator-filter, -invalid-address). Net coverage strictly increases. The now-unused mockPrisma / VALID_ADDRESS bindings in that file were dropped with it, and a comment points to the new location.

Happy to restore it if you'd rather the duplication stayed.

Testing

  • pnpm lint — clean
  • pnpm build — clean
  • pnpm exec prisma generate when schema or generated types changed — n/a, no schema change
  • pnpm exec prettier --check on both touched files — clean
  • jest wallet-activity-empty-history.integration.test.ts — 11/11 pass
  • jest wallet-activity-invalid-address.integration.test.ts — 4/4 pass

CI runs lint and build only, not the test suite, so the new tests were verified locally.

On the rest of the module suite

Running the whole src/modules/wallets directory locally shows failures I want to be transparent about, none of them from this PR:

  • The six suites in src/modules/wallets/__tests__/ need a real Postgres (they call prisma.*.deleteMany), and I have no database running locally.
  • Separately, the first test in a suite can exceed the 30s jest timeout on a loaded machine. I confirmed this is pre-existing and unrelated by checking out wallet-activity-invalid-address.integration.test.ts exactly as it is on main, with none of my changes, and reproducing the same timeout on should return 400 for address with wrong prefix.

Both touched files pass consistently when run with --runInBand (43s and 41s respectively).

Checklist

  • Linked issue or backlog item
  • No secrets or live credentials added
  • Docs updated if setup or env changed — n/a
  • Change is scoped to one problem

Covers GET /api/v1/wallets/:address/activity for a wallet that has never
traded: 200 with an empty items array, hasMore false and nextCursor
present and null — not a 404 and not an error envelope.

Requests go through the real Express app via supertest so routing,
address validation, the controller, the service and JSON serialization
all participate; only Prisma is mocked, matching the other integration
tests in this module.

Beyond the four acceptance criteria the suite asserts the query is
scoped to the requested wallet (otherwise an empty page would be
incidental rather than an answer about that address), pins the wire
format for nextCursor directly, and checks the contract holds across
type, creator_id, pagination and cursor variants.

wallet-activity-invalid-address.integration.test.ts already held a
partial version of this case which asserted neither nextCursor nor the
cursor branch, in a file scoped to malformed addresses. It moved here
rather than being duplicated.
@drips-wave

drips-wave Bot commented Jul 26, 2026

Copy link
Copy Markdown

@Fury03 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@Chucks1093
Chucks1093 merged commit 9310712 into accesslayerorg:main Jul 26, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add integration test for trade history endpoint returning empty array for a wallet with no trades

2 participants