Skip to content

feat: implement add paginated auctions endpoint and user vaults retrieval API#459

Merged
ryzen-xp merged 2 commits into
Alien-Protocol:devfrom
Jonathan-ai9:dev
Apr 29, 2026
Merged

feat: implement add paginated auctions endpoint and user vaults retrieval API#459
ryzen-xp merged 2 commits into
Alien-Protocol:devfrom
Jonathan-ai9:dev

Conversation

@Jonathan-ai9
Copy link
Copy Markdown

@Jonathan-ai9 Jonathan-ai9 commented Apr 28, 2026

Closes #439
Closes #438

PR: Add Auctions Pagination + User Vaults Endpoint
Summary

This PR introduces two new backend endpoints to improve data accessibility for clients. It combines work from #439 and #438, enabling efficient auction listing with pagination and retrieval of user-specific vault data.

📦 Auctions Endpoint (Pagination + Filtering)
Implemented GET /auctions?page=1&limit=20
Added optional status query filter (active, closed, claimed)
Returns auction list with:
id
username
highest_bid
end_time
status
Default limit set to 20, capped at 100
Ensures consistent pagination behavior
🏦 User Vaults Endpoint
Implemented GET /users/:id/vaults
Fetches all escrow vaults associated with a given user
Returns vault list with:
id
balance
status
created_at
Returns 404 if the user does not exist
🧪 Testing
Added unit and e2e tests for both endpoints
Covered:
Pagination correctness
Status filtering behavior
Vault retrieval success and failure (404) cases
All tests pass (npm run test)
🔧 Notes
No breaking changes introduced
Designed to reduce reliance on on-chain queries for common client operations
Improves backend API usability and performance

Summary by CodeRabbit

  • New Features

    • Added paginated auctions endpoint with optional status filtering (open, closed, or claimed).
    • Added endpoint to retrieve vaults for a specified user, including vault details.
  • Tests

    • Comprehensive test coverage added for both features, validating pagination controls, status filtering, and vault retrieval functionality across multiple scenarios.

@drips-wave
Copy link
Copy Markdown

drips-wave Bot commented Apr 28, 2026

@Jonathan-ai9 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

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 28, 2026

📝 Walkthrough

Walkthrough

This PR introduces two new paginated API endpoints: GET /stellar/auctions for listing auctions with optional status filtering, and GET /users/:id/vaults for retrieving user vaults. Both features include DTOs, controller routes, service methods, and comprehensive unit and e2e test coverage.

Changes

Cohort / File(s) Summary
Auction Listing DTO
backend/src/auction/dto/auction.dto.ts
New AuctionListItemDto class with Swagger-annotated fields (id, username, highestBid, endTime, status) and enum constraint for status values.
Auction Listing Controller & Service
backend/src/stellar/stellar.controller.ts, backend/src/stellar/stellar.service.ts
New GET /stellar/auctions endpoint injecting StellarService; accepts paginated page and limit (capped at 100) and optional status filter; service implements getAuctions using mock auction data with in-memory filtering and pagination.
Auction Listing Tests
backend/src/stellar/stellar.service.spec.ts, backend/test/auctions.e2e-spec.ts
Unit tests for StellarService.getAuctions covering pagination, status filtering, and limit handling; e2e tests for endpoint behavior including pagination, status filtering, and invalid status handling.
Vault Listing DTO
backend/src/vault/dto/vault.dto.ts
New VaultListItemDto class with Swagger-annotated fields (id, balance, status, createdAt) and enum constraint for status values.
Vault Listing Controller & Service
backend/src/vault/vault.controller.ts, backend/src/vault/vault.service.ts
New GET /users/:id/vaults endpoint with StellarAddressPipe validation; service implements getVaultsByOwner querying Prisma, mapping responses, and throwing NotFoundException if no vaults found.
Vault Module Configuration
backend/src/vault/vault.module.ts
Registers VaultService in module providers array.
Vault Listing Tests
backend/src/vault/vault.service.spec.ts, backend/test/vault.e2e-spec.ts
Unit tests verifying getVaultsByOwner maps Prisma results to DTOs and throws exception on empty results; e2e tests validating successful vault retrieval and 404 for unknown users.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant StellarController
    participant StellarService
    participant MockData

    Client->>StellarController: GET /stellar/auctions?page=1&limit=20&status=open
    StellarController->>StellarService: getAuctions(1, 20, 'open')
    StellarService->>MockData: retrieve in-memory auctions
    MockData-->>StellarService: auction records
    StellarService->>StellarService: filter by status='open'
    StellarService->>StellarService: apply pagination slice
    StellarService-->>StellarController: filtered & paginated results
    StellarController-->>Client: 200 + AuctionListItemDto[]
Loading
sequenceDiagram
    participant Client
    participant VaultController
    participant StellarAddressPipe
    participant VaultService
    participant Prisma DB

    Client->>VaultController: GET /users/:id/vaults
    VaultController->>StellarAddressPipe: validate id parameter
    StellarAddressPipe-->>VaultController: validated address
    VaultController->>VaultService: getVaultsByOwner(id)
    VaultService->>Prisma DB: findMany where owner=id
    Prisma DB-->>VaultService: vault records
    alt vaults found
        VaultService->>VaultService: map to VaultListItemDto[]
        VaultService-->>VaultController: mapped DTOs
        VaultController-->>Client: 200 + VaultListItemDto[]
    else vaults not found
        VaultService-->>VaultController: NotFoundException
        VaultController-->>Client: 404 Not Found
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Alien-Protocol/Alien-Protocol#426 – Implements the foundational StellarService and module that the new getAuctions endpoint depends on.
  • Alien-Protocol/Alien-Protocol#425 – Implements the foundational VaultService, VaultController, and VaultModule that this PR extends with the new getVaultsByOwner method and endpoint.
  • Alien-Protocol/Alien-Protocol#423 – Introduces similar auction and vault DTO patterns and controller routing structures that align with this PR's additions.

Suggested labels

Approved

Poem

🐰 Two endpoints hop into the warren,
Auctions paginate, vaults beckon,
Mock data dances, Prisma gleams bright,
Tests validate the feature's might!
With filtering, with grace, with care,
The API blooms beyond compare!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes both main changes: implementing a paginated auctions endpoint and a user vaults retrieval API, matching the primary objectives.
Linked Issues check ✅ Passed All coding requirements from linked issues #439 and #438 are met: paginated auctions with status filtering, vault retrieval with 404 handling, proper DTOs, unit and e2e tests, and pagination constraints are all implemented.
Out of Scope Changes check ✅ Passed All changes are directly aligned with the linked issues. The PR includes new DTOs, controllers, services, and comprehensive tests—all within the scope of the two objectives with no extraneous modifications.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 12

🧹 Nitpick comments (1)
backend/test/auctions.e2e-spec.ts (1)

44-52: Limit-cap test does not actually validate the cap behavior.

Line 50 notes the mock only returns 3 items, so this case currently can’t fail even if cap logic regresses. Consider seeding >100 auctions in e2e fixture data or asserting via a deterministic test double in unit tests.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/test/auctions.e2e-spec.ts` around lines 44 - 52, The test "GET
/stellar/auctions - respects limit cap of 100" doesn't actually validate the cap
because the test fixture only returns 3 items; update the test so it
deterministically receives >100 items and assert the response is capped at 100:
either seed the e2e fixture that the test suite uses with 150+ auction records
in the setup/seed function (the code that populates auctions for
auctions.e2e-spec.ts) or, if using a test double, have the
AuctionsService/findAll (or whichever service method the controller uses) mock
return 150 items, then change the expectation to assert Array.isArray(res.body)
is true and expect(res.body.length).toBe(100) for the GET
'/stellar/auctions?limit=200' request against app.getHttpServer().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/src/auction/dto/auction.dto.ts`:
- Around line 29-44: The DTO field names in AuctionListItemDto don't match the
API contract: change the properties highestBid and endTime to use snake_case
(highest_bid and end_time) and update their `@ApiProperty` metadata
(description/examples/enum if needed) so the serialized response matches the
contract; adjust any code/tests that reference
AuctionListItemDto.{highestBid,endTime} to the new names (or alternatively add
explicit `@Expose` mappings if you must keep camelCase internally).

In `@backend/src/stellar/stellar.controller.ts`:
- Around line 44-45: The controller currently uses `@Controller`('stellar')
combined with `@Get`('auctions') which results in the route /stellar/auctions
rather than the required /auctions; update the route declarations so the
endpoint is exposed at /auctions (e.g., remove or make empty the 'stellar'
prefix on the class-level `@Controller` decorator or move the auctions handler
into a controller that has no prefix) — locate the class with the
`@Controller`('stellar') annotation and the method decorated with `@Get`('auctions')
and adjust the class- or method-level decorator so the final path is /auctions.
- Around line 74-75: The page/limit parsing in stellar.controller.ts converts
raw query strings to numbers with parseInt into local variables p and l but
lacks validation and lower-bound enforcement; update the controller to validate
and coerce these inputs (use NestJS ParseIntPipe on the query DTO or controller
params for page and limit) and replace the manual parseInt logic (the lines that
set p and l) with bounded math such as p = Math.max(parsedPage, 1) and l =
Math.max(1, Math.min(parsedLimit, 100)) so non-numeric values are rejected and
zero/negative limits/pages are clamped to valid ranges.

In `@backend/src/stellar/stellar.service.spec.ts`:
- Around line 56-60: The test "should cap limit at 100" doesn't assert the
capping behavior; update the test that calls service.getAuctions(1, 200) to
assert the returned array length is capped at 100 (e.g.,
expect(result.length).toBeLessThanOrEqual(100)) and, given the test fixture/mock
has 3 items, also assert the length equals the mock count (e.g.,
expect(result.length).toBe(3)). Locate this in the stellar.service.spec.ts test
for service.getAuctions and add those assertions.

In `@backend/src/stellar/stellar.service.ts`:
- Around line 53-72: The getAuctions method currently returns a hardcoded
mockAuctions array and ignores real data; replace the mock implementation in
getAuctions with a real data fetch and proper filtering/pagination: call the
auction contract or database client used elsewhere in this service (e.g., the
same client used by other Stellar methods), retrieve the full auction list or a
paginated query, apply the optional status filter on the fetched results, and
then apply pagination (start = (page-1)*limit, end = start+limit) before
returning; ensure you remove or gate the mockAuctions variable and reuse
existing service helpers/clients in stellar.service.ts (refer to getAuctions and
any contract/database client fields) so the endpoint returns real auction data.

In `@backend/src/vault/dto/vault.dto.ts`:
- Around line 58-59: Rename the DTO property createdAt to created_at to match
the API contract: update the field name in vault.dto.ts (the property currently
decorated with `@ApiProperty` describing "Timestamp when vault was created") and
adjust any references to Vault DTO consumers (serialization, mappers, tests) to
use created_at; preserve the ApiProperty metadata and example value so the
generated OpenAPI schema reflects the correct snake_case field.

In `@backend/src/vault/vault.controller.ts`:
- Line 57: The route decorator on VaultController is producing GET
/vault/users/:id/vaults because of `@Controller`('vault') combined with
`@Get`('users/:id/vaults'); update the routing to match the public contract by
making the GET path absolute (e.g., change `@Get`('users/:id/vaults') to
`@Get`('/users/:id/vaults')) or move the handler to the appropriate controller
without the 'vault' prefix; ensure you update the handler method in
vault.controller.ts and any related tests or docs that expect GET
/users/:id/vaults.

In `@backend/src/vault/vault.service.spec.ts`:
- Around line 132-137: The test currently mocks prisma.vault.findMany to return
[] but asserts a NotFoundException from service.getVaultsByOwner — change this
test to assert that getVaultsByOwner resolves to an empty array when
prisma.vault.findMany returns [] (i.e., expect(await
service.getVaultsByOwner(...)).toEqual([]) or use resolves.toEqual([])
referencing service.getVaultsByOwner and prisma.vault.findMany), and add a
separate test that mocks the user lookup (e.g., prisma.user.findUnique or the
method your service uses to check existence) to return null and then assert
service.getVaultsByOwner throws NotFoundException for a non-existent user.

In `@backend/src/vault/vault.service.ts`:
- Around line 150-152: The current check throwing NotFoundException when
vaults.length === 0 conflates "user doesn't exist" with "user exists but has no
vaults"; instead either (A) remove the throw and return the empty vaults array
so callers receive [] for users with no vaults, or (B) add an explicit owner
existence check before querying/throwing: inject the UserService or User
repository into the vault service, call its lookup (e.g.,
userService.findById(owner) or userRepository.findOne({ id: owner })), if that
lookup returns null then throw NotFoundException, otherwise return the (possibly
empty) vaults array; update the code around the vaults.length === 0 branch to
implement one of these two behaviors and keep the NotFoundException only for the
true "user not found" case.

In `@backend/test/auctions.e2e-spec.ts`:
- Around line 9-16: The test suite initializes an INestApplication in beforeEach
(app = moduleFixture.createNestApplication(); await app.init();) but never
closes it, leading to leaked handles; add a teardown that calls await
app.close() (e.g., in an afterEach or afterAll) and ensure the INestApplication
instance (app) is defined/accessible to the teardown so each test run properly
closes the app after initialization.
- Around line 54-85: The tests for the /stellar/auctions endpoint use the wrong
status value and miss coverage for "claimed": update the test named "GET
/stellar/auctions - filters by status (open)" to request status=active (not
open), add a new test case that calls /stellar/auctions?status=claimed and
asserts returned auctions have auction.status === 'claimed', and ensure the
"handles invalid status gracefully" test still validates the endpoint's behavior
for unknown status values; target the test suite functions in this file that
call request(app.getHttpServer()) for the /stellar/auctions endpoint and update
their query strings and assertions accordingly.

In `@backend/test/vault.e2e-spec.ts`:
- Around line 18-30: The test "GET /vault/users/:id/vaults - returns vault list
for existing user" currently lets an empty array pass because property
assertions are guarded by if (res.body.length > 0); change the assertions to
require a non-empty array (e.g., expect(res.body.length).toBeGreaterThan(0) or
expect(res.body).not.toHaveLength(0)) and then assert the first item's fields
(id, balance, status, createdAt) unconditionally so the test fails when no
vaults are returned for the existing user.

---

Nitpick comments:
In `@backend/test/auctions.e2e-spec.ts`:
- Around line 44-52: The test "GET /stellar/auctions - respects limit cap of
100" doesn't actually validate the cap because the test fixture only returns 3
items; update the test so it deterministically receives >100 items and assert
the response is capped at 100: either seed the e2e fixture that the test suite
uses with 150+ auction records in the setup/seed function (the code that
populates auctions for auctions.e2e-spec.ts) or, if using a test double, have
the AuctionsService/findAll (or whichever service method the controller uses)
mock return 150 items, then change the expectation to assert
Array.isArray(res.body) is true and expect(res.body.length).toBe(100) for the
GET '/stellar/auctions?limit=200' request against app.getHttpServer().
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fff09b35-3766-4a49-8528-4549ff20a29b

📥 Commits

Reviewing files that changed from the base of the PR and between 8b75f9b and d70fb31.

📒 Files selected for processing (11)
  • backend/src/auction/dto/auction.dto.ts
  • backend/src/stellar/stellar.controller.ts
  • backend/src/stellar/stellar.service.spec.ts
  • backend/src/stellar/stellar.service.ts
  • backend/src/vault/dto/vault.dto.ts
  • backend/src/vault/vault.controller.ts
  • backend/src/vault/vault.module.ts
  • backend/src/vault/vault.service.spec.ts
  • backend/src/vault/vault.service.ts
  • backend/test/auctions.e2e-spec.ts
  • backend/test/vault.e2e-spec.ts

Comment on lines +29 to +44
export class AuctionListItemDto {
@ApiProperty({ description: 'Auction ID', example: 1 })
id: number;

@ApiProperty({ description: 'Username being auctioned', example: 'satoshi' })
username: string;

@ApiProperty({ description: 'Current highest bid in XLM', example: '150.00' })
highestBid: string;

@ApiProperty({ description: 'Auction end time (Unix timestamp)', example: 1745000000 })
endTime: number;

@ApiProperty({ description: 'Auction status', example: 'open', enum: ['open', 'closed', 'claimed'] })
status: string;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

API response field names in AuctionListItemDto don’t match the stated endpoint contract.

The DTO exposes highestBid and endTime, while the linked objective specifies highest_bid and end_time. This creates a contract drift for clients and docs.

Proposed contract-aligned DTO shape
 export class AuctionListItemDto {
   `@ApiProperty`({ description: 'Auction ID', example: 1 })
   id: number;

   `@ApiProperty`({ description: 'Username being auctioned', example: 'satoshi' })
   username: string;

-  `@ApiProperty`({ description: 'Current highest bid in XLM', example: '150.00' })
-  highestBid: string;
+  `@ApiProperty`({ description: 'Current highest bid in XLM', example: '150.00' })
+  highest_bid: string;

-  `@ApiProperty`({ description: 'Auction end time (Unix timestamp)', example: 1745000000 })
-  endTime: number;
+  `@ApiProperty`({ description: 'Auction end time (Unix timestamp)', example: 1745000000 })
+  end_time: number;

   `@ApiProperty`({ description: 'Auction status', example: 'open', enum: ['open', 'closed', 'claimed'] })
   status: string;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/auction/dto/auction.dto.ts` around lines 29 - 44, The DTO field
names in AuctionListItemDto don't match the API contract: change the properties
highestBid and endTime to use snake_case (highest_bid and end_time) and update
their `@ApiProperty` metadata (description/examples/enum if needed) so the
serialized response matches the contract; adjust any code/tests that reference
AuctionListItemDto.{highestBid,endTime} to the new names (or alternatively add
explicit `@Expose` mappings if you must keep camelCase internally).

Comment on lines +44 to +45
@Get('auctions')
@ApiOperation({ summary: 'Get paginated list of auctions with optional status filter' })
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Endpoint path differs from the stated contract.

With @Controller('stellar') + @Get('auctions'), this is /stellar/auctions, while the linked objective specifies /auctions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/stellar/stellar.controller.ts` around lines 44 - 45, The
controller currently uses `@Controller`('stellar') combined with `@Get`('auctions')
which results in the route /stellar/auctions rather than the required /auctions;
update the route declarations so the endpoint is exposed at /auctions (e.g.,
remove or make empty the 'stellar' prefix on the class-level `@Controller`
decorator or move the auctions handler into a controller that has no prefix) —
locate the class with the `@Controller`('stellar') annotation and the method
decorated with `@Get`('auctions') and adjust the class- or method-level decorator
so the final path is /auctions.

Comment on lines +74 to +75
const p = page ? parseInt(page, 10) : 1;
const l = limit ? Math.min(parseInt(limit, 10), 100) : 20;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n backend/src/stellar/stellar.controller.ts | head -100

Repository: Alien-Protocol/Alien-Protocol

Length of output: 3311


🏁 Script executed:

rg "ParseIntPipe|DefaultValuePipe" --type ts --type tsx -A 2 -B 2 | head -50

Repository: Alien-Protocol/Alien-Protocol

Length of output: 101


🏁 Script executed:

rg "ParseIntPipe|DefaultValuePipe" --type ts -A 2 -B 2

Repository: Alien-Protocol/Alien-Protocol

Length of output: 55


🏁 Script executed:

cat package.json | grep -A 5 -B 5 "@nestjs"

Repository: Alien-Protocol/Alien-Protocol

Length of output: 55


🏁 Script executed:

find . -name "package.json" -type f | head -5

Repository: Alien-Protocol/Alien-Protocol

Length of output: 111


🏁 Script executed:

cat backend/package.json | grep -A 20 "dependencies"

Repository: Alien-Protocol/Alien-Protocol

Length of output: 479


page/limit parsing lacks input validation and lower-bound enforcement.

Non-numeric values (e.g., page="abc") result in NaN being passed to pagination logic. Zero or negative values are not rejected: page="0" remains truthy as a string and produces p=0, while limit="-5" produces l=-5, both breaking pagination bounds.

Use ParseIntPipe to validate integer format and Math.max/Math.min to enforce bounds:

Suggested fix
-import { Controller, Get, Param, Query } from '@nestjs/common';
+import { Controller, DefaultValuePipe, Get, Param, ParseIntPipe, Query } from '@nestjs/common';
@@
 async getAuctions(
-  `@Query`('page') page?: string,
-  `@Query`('limit') limit?: string,
+  `@Query`('page', new DefaultValuePipe(1), ParseIntPipe) page: number,
+  `@Query`('limit', new DefaultValuePipe(20), ParseIntPipe) limit: number,
   `@Query`('status') status?: string,
 ): Promise<AuctionListItemDto[]> {
-  const p = page ? parseInt(page, 10) : 1;
-  const l = limit ? Math.min(parseInt(limit, 10), 100) : 20;
+  const p = Math.max(page, 1);
+  const l = Math.min(Math.max(limit, 1), 100);
   return this.stellarService.getAuctions(p, l, status);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/stellar/stellar.controller.ts` around lines 74 - 75, The
page/limit parsing in stellar.controller.ts converts raw query strings to
numbers with parseInt into local variables p and l but lacks validation and
lower-bound enforcement; update the controller to validate and coerce these
inputs (use NestJS ParseIntPipe on the query DTO or controller params for page
and limit) and replace the manual parseInt logic (the lines that set p and l)
with bounded math such as p = Math.max(parsedPage, 1) and l = Math.max(1,
Math.min(parsedLimit, 100)) so non-numeric values are rejected and zero/negative
limits/pages are clamped to valid ranges.

Comment on lines +56 to +60
it('should cap limit at 100', async () => {
const result = await service.getAuctions(1, 200);
expect(Array.isArray(result)).toBe(true);
// Our mock only has 3 items, but limit shouldn't matter here
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

“cap limit at 100” test has no cap assertion.

This test currently only checks array-ness; it does not validate limit capping behavior.

Assertion that validates capping semantics
     it('should cap limit at 100', async () => {
-      const result = await service.getAuctions(1, 200);
-      expect(Array.isArray(result)).toBe(true);
-      // Our mock only has 3 items, but limit shouldn't matter here
+      const capped = await service.getAuctions(1, 100);
+      const uncapped = await service.getAuctions(1, 200);
+      expect(uncapped).toEqual(capped);
     });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('should cap limit at 100', async () => {
const result = await service.getAuctions(1, 200);
expect(Array.isArray(result)).toBe(true);
// Our mock only has 3 items, but limit shouldn't matter here
});
it('should cap limit at 100', async () => {
const capped = await service.getAuctions(1, 100);
const uncapped = await service.getAuctions(1, 200);
expect(uncapped).toEqual(capped);
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/stellar/stellar.service.spec.ts` around lines 56 - 60, The test
"should cap limit at 100" doesn't assert the capping behavior; update the test
that calls service.getAuctions(1, 200) to assert the returned array length is
capped at 100 (e.g., expect(result.length).toBeLessThanOrEqual(100)) and, given
the test fixture/mock has 3 items, also assert the length equals the mock count
(e.g., expect(result.length).toBe(3)). Locate this in the
stellar.service.spec.ts test for service.getAuctions and add those assertions.

Comment on lines +53 to +72
async getAuctions(page: number = 1, limit: number = 20, status?: string): Promise<any[]> {
// Placeholder: actual implementation would call the auction contract's list_auctions method
// For now, return mock data to satisfy the interface
const mockAuctions = [
{ id: 1, username: 'satoshi', highestBid: '150.00', endTime: 1745000000, status: 'open' },
{ id: 2, username: 'vitalik', highestBid: '200.00', endTime: 1746000000, status: 'closed' },
{ id: 3, username: 'alice', highestBid: '75.00', endTime: 1747000000, status: 'open' },
];

// Apply status filter if provided
let filtered = mockAuctions;
if (status) {
filtered = mockAuctions.filter((a) => a.status === status);
}

// Apply pagination
const start = (page - 1) * limit;
const end = start + limit;
return filtered.slice(start, end);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

getAuctions is not actually implemented (hardcoded mock response).

The method currently ignores real data sources and always returns a static list, so the endpoint cannot satisfy real pagination/filtering against actual auctions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/stellar/stellar.service.ts` around lines 53 - 72, The getAuctions
method currently returns a hardcoded mockAuctions array and ignores real data;
replace the mock implementation in getAuctions with a real data fetch and proper
filtering/pagination: call the auction contract or database client used
elsewhere in this service (e.g., the same client used by other Stellar methods),
retrieve the full auction list or a paginated query, apply the optional status
filter on the fetched results, and then apply pagination (start =
(page-1)*limit, end = start+limit) before returning; ensure you remove or gate
the mockAuctions variable and reuse existing service helpers/clients in
stellar.service.ts (refer to getAuctions and any contract/database client
fields) so the endpoint returns real auction data.

Comment on lines +132 to +137
it('should throw NotFoundException when no vaults found for owner', async () => {
(prisma.vault.findMany as jest.Mock).mockResolvedValue([]);

await expect(service.getVaultsByOwner('GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN')).rejects.toThrow(
NotFoundException,
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

The empty-vault case is asserting the wrong behavior.

This test enforces 404 when findMany returns [], but the stated contract is 404 when the user does not exist. An existing user with no vaults should typically return an empty list.

Suggested test adjustment
-    it('should throw NotFoundException when no vaults found for owner', async () => {
+    it('should return empty list when owner exists but has no vaults', async () => {
       (prisma.vault.findMany as jest.Mock).mockResolvedValue([]);

-      await expect(service.getVaultsByOwner('GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN')).rejects.toThrow(
-        NotFoundException,
-      );
+      await expect(
+        service.getVaultsByOwner('GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN'),
+      ).resolves.toEqual([]);
     });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('should throw NotFoundException when no vaults found for owner', async () => {
(prisma.vault.findMany as jest.Mock).mockResolvedValue([]);
await expect(service.getVaultsByOwner('GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN')).rejects.toThrow(
NotFoundException,
);
it('should return empty list when owner exists but has no vaults', async () => {
(prisma.vault.findMany as jest.Mock).mockResolvedValue([]);
await expect(
service.getVaultsByOwner('GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN'),
).resolves.toEqual([]);
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/vault/vault.service.spec.ts` around lines 132 - 137, The test
currently mocks prisma.vault.findMany to return [] but asserts a
NotFoundException from service.getVaultsByOwner — change this test to assert
that getVaultsByOwner resolves to an empty array when prisma.vault.findMany
returns [] (i.e., expect(await service.getVaultsByOwner(...)).toEqual([]) or use
resolves.toEqual([]) referencing service.getVaultsByOwner and
prisma.vault.findMany), and add a separate test that mocks the user lookup
(e.g., prisma.user.findUnique or the method your service uses to check
existence) to return null and then assert service.getVaultsByOwner throws
NotFoundException for a non-existent user.

Comment on lines +150 to +152
if (vaults.length === 0) {
throw new NotFoundException(`No vaults found for owner ${owner}`);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Check if a User model exists =="
fd -i "schema.prisma" --exec sed -n '/model User/,/}/p' {}

echo
echo "== Find user-existence checks near vault retrieval flow =="
rg -n -C3 "getVaultsByOwner|No vaults found for owner|findUnique\\(|users/:id/vaults|has no vaults|User not found"

Repository: Alien-Protocol/Alien-Protocol

Length of output: 6778


Line 150-152: Cannot distinguish between non-existent user and user with zero vaults

The code throws NotFoundException when vaults.length === 0, but this conflates two scenarios:

  • User does not exist (should return 404)
  • User exists but owns no vaults (should return [] with 200)

Since no User entity exists in the schema and there is no explicit user existence check, the implementation relies on the absence of vaults as a proxy for a non-existent user. This is semantically incorrect and the API contract (line 65 of vault.controller.ts: "User not found or has no vaults") conflates both cases.

Either add an explicit user/owner validation step before querying vaults, or change the behavior to return an empty array for users with no vaults instead of throwing an error.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/vault/vault.service.ts` around lines 150 - 152, The current check
throwing NotFoundException when vaults.length === 0 conflates "user doesn't
exist" with "user exists but has no vaults"; instead either (A) remove the throw
and return the empty vaults array so callers receive [] for users with no
vaults, or (B) add an explicit owner existence check before querying/throwing:
inject the UserService or User repository into the vault service, call its
lookup (e.g., userService.findById(owner) or userRepository.findOne({ id: owner
})), if that lookup returns null then throw NotFoundException, otherwise return
the (possibly empty) vaults array; update the code around the vaults.length ===
0 branch to implement one of these two behaviors and keep the NotFoundException
only for the true "user not found" case.

Comment on lines +9 to +16
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();

app = moduleFixture.createNestApplication();
await app.init();
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

INestApplication is never closed between tests.

The app is initialized in beforeEach but not closed, which can leak handles and cause flaky e2e runs.

Proposed cleanup
 describe('Auctions (e2e)', () => {
   let app: INestApplication;

   beforeEach(async () => {
@@
     app = moduleFixture.createNestApplication();
     await app.init();
   });
+
+  afterEach(async () => {
+    await app.close();
+  });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/test/auctions.e2e-spec.ts` around lines 9 - 16, The test suite
initializes an INestApplication in beforeEach (app =
moduleFixture.createNestApplication(); await app.init();) but never closes it,
leading to leaked handles; add a teardown that calls await app.close() (e.g., in
an afterEach or afterAll) and ensure the INestApplication instance (app) is
defined/accessible to the teardown so each test run properly closes the app
after initialization.

Comment on lines +54 to +85
it('GET /stellar/auctions - filters by status (open)', () => {
return request(app.getHttpServer())
.get('/stellar/auctions?status=open')
.expect(200)
.expect((res) => {
expect(Array.isArray(res.body)).toBe(true);
res.body.forEach((auction) => {
expect(auction.status).toBe('open');
});
});
});

it('GET /stellar/auctions - filters by status (closed)', () => {
return request(app.getHttpServer())
.get('/stellar/auctions?status=closed')
.expect(200)
.expect((res) => {
expect(Array.isArray(res.body)).toBe(true);
res.body.forEach((auction) => {
expect(auction.status).toBe('closed');
});
});
});

it('GET /stellar/auctions - handles invalid status gracefully', () => {
return request(app.getHttpServer())
.get('/stellar/auctions?status=invalid')
.expect(200)
.expect((res) => {
expect(Array.isArray(res.body)).toBe(true);
});
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Status filter tests are misaligned with the endpoint contract.

Line 56 uses status=open, but the objective defines active|closed|claimed. Also, there is no claimed coverage.

Proposed test updates
-  it('GET /stellar/auctions - filters by status (open)', () => {
+  it('GET /stellar/auctions - filters by status (active)', () => {
     return request(app.getHttpServer())
-      .get('/stellar/auctions?status=open')
+      .get('/stellar/auctions?status=active')
       .expect(200)
       .expect((res) => {
         expect(Array.isArray(res.body)).toBe(true);
         res.body.forEach((auction) => {
-          expect(auction.status).toBe('open');
+          expect(auction.status).toBe('active');
         });
       });
   });

+  it('GET /stellar/auctions - filters by status (claimed)', () => {
+    return request(app.getHttpServer())
+      .get('/stellar/auctions?status=claimed')
+      .expect(200)
+      .expect((res) => {
+        expect(Array.isArray(res.body)).toBe(true);
+        res.body.forEach((auction) => {
+          expect(auction.status).toBe('claimed');
+        });
+      });
+  });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('GET /stellar/auctions - filters by status (open)', () => {
return request(app.getHttpServer())
.get('/stellar/auctions?status=open')
.expect(200)
.expect((res) => {
expect(Array.isArray(res.body)).toBe(true);
res.body.forEach((auction) => {
expect(auction.status).toBe('open');
});
});
});
it('GET /stellar/auctions - filters by status (closed)', () => {
return request(app.getHttpServer())
.get('/stellar/auctions?status=closed')
.expect(200)
.expect((res) => {
expect(Array.isArray(res.body)).toBe(true);
res.body.forEach((auction) => {
expect(auction.status).toBe('closed');
});
});
});
it('GET /stellar/auctions - handles invalid status gracefully', () => {
return request(app.getHttpServer())
.get('/stellar/auctions?status=invalid')
.expect(200)
.expect((res) => {
expect(Array.isArray(res.body)).toBe(true);
});
});
it('GET /stellar/auctions - filters by status (active)', () => {
return request(app.getHttpServer())
.get('/stellar/auctions?status=active')
.expect(200)
.expect((res) => {
expect(Array.isArray(res.body)).toBe(true);
res.body.forEach((auction) => {
expect(auction.status).toBe('active');
});
});
});
it('GET /stellar/auctions - filters by status (claimed)', () => {
return request(app.getHttpServer())
.get('/stellar/auctions?status=claimed')
.expect(200)
.expect((res) => {
expect(Array.isArray(res.body)).toBe(true);
res.body.forEach((auction) => {
expect(auction.status).toBe('claimed');
});
});
});
it('GET /stellar/auctions - filters by status (closed)', () => {
return request(app.getHttpServer())
.get('/stellar/auctions?status=closed')
.expect(200)
.expect((res) => {
expect(Array.isArray(res.body)).toBe(true);
res.body.forEach((auction) => {
expect(auction.status).toBe('closed');
});
});
});
it('GET /stellar/auctions - handles invalid status gracefully', () => {
return request(app.getHttpServer())
.get('/stellar/auctions?status=invalid')
.expect(200)
.expect((res) => {
expect(Array.isArray(res.body)).toBe(true);
});
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/test/auctions.e2e-spec.ts` around lines 54 - 85, The tests for the
/stellar/auctions endpoint use the wrong status value and miss coverage for
"claimed": update the test named "GET /stellar/auctions - filters by status
(open)" to request status=active (not open), add a new test case that calls
/stellar/auctions?status=claimed and asserts returned auctions have
auction.status === 'claimed', and ensure the "handles invalid status gracefully"
test still validates the endpoint's behavior for unknown status values; target
the test suite functions in this file that call request(app.getHttpServer()) for
the /stellar/auctions endpoint and update their query strings and assertions
accordingly.

Comment on lines +18 to +30
it('GET /vault/users/:id/vaults - returns vault list for existing user', () => {
return request(app.getHttpServer())
.get('/vault/users/GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN/vaults')
.expect(200)
.expect((res) => {
expect(Array.isArray(res.body)).toBe(true);
if (res.body.length > 0) {
expect(res.body[0]).toHaveProperty('id');
expect(res.body[0]).toHaveProperty('balance');
expect(res.body[0]).toHaveProperty('status');
expect(res.body[0]).toHaveProperty('createdAt');
}
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Existing-user e2e test can pass even when no vaults are returned.

Because assertions are wrapped in if (res.body.length > 0), an empty array still passes this “existing user” scenario.

Stronger assertion
       .expect(200)
       .expect((res) => {
         expect(Array.isArray(res.body)).toBe(true);
-        if (res.body.length > 0) {
-          expect(res.body[0]).toHaveProperty('id');
-          expect(res.body[0]).toHaveProperty('balance');
-          expect(res.body[0]).toHaveProperty('status');
-          expect(res.body[0]).toHaveProperty('createdAt');
-        }
+        expect(res.body.length).toBeGreaterThan(0);
+        expect(res.body[0]).toHaveProperty('id');
+        expect(res.body[0]).toHaveProperty('balance');
+        expect(res.body[0]).toHaveProperty('status');
+        expect(res.body[0]).toHaveProperty('createdAt');
       });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('GET /vault/users/:id/vaults - returns vault list for existing user', () => {
return request(app.getHttpServer())
.get('/vault/users/GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN/vaults')
.expect(200)
.expect((res) => {
expect(Array.isArray(res.body)).toBe(true);
if (res.body.length > 0) {
expect(res.body[0]).toHaveProperty('id');
expect(res.body[0]).toHaveProperty('balance');
expect(res.body[0]).toHaveProperty('status');
expect(res.body[0]).toHaveProperty('createdAt');
}
});
it('GET /vault/users/:id/vaults - returns vault list for existing user', () => {
return request(app.getHttpServer())
.get('/vault/users/GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN/vaults')
.expect(200)
.expect((res) => {
expect(Array.isArray(res.body)).toBe(true);
expect(res.body.length).toBeGreaterThan(0);
expect(res.body[0]).toHaveProperty('id');
expect(res.body[0]).toHaveProperty('balance');
expect(res.body[0]).toHaveProperty('status');
expect(res.body[0]).toHaveProperty('createdAt');
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/test/vault.e2e-spec.ts` around lines 18 - 30, The test "GET
/vault/users/:id/vaults - returns vault list for existing user" currently lets
an empty array pass because property assertions are guarded by if
(res.body.length > 0); change the assertions to require a non-empty array (e.g.,
expect(res.body.length).toBeGreaterThan(0) or
expect(res.body).not.toHaveLength(0)) and then assert the first item's fields
(id, balance, status, createdAt) unconditionally so the test fails when no
vaults are returned for the existing user.

@ryzen-xp ryzen-xp added the APPROVED This PR is ready for merging . label Apr 29, 2026
@ryzen-xp ryzen-xp merged commit 7eb75da into Alien-Protocol:dev Apr 29, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

APPROVED This PR is ready for merging .

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: add GET /auctions endpoint with pagination to backend feat: add GET /users/:id/vaults endpoint to backend

2 participants