Skip to content

Conversation

gfyrag
Copy link
Collaborator

@gfyrag gfyrag commented Jun 30, 2025

No description provided.

@gfyrag gfyrag requested a review from a team as a code owner June 30, 2025 10:38
Copy link

coderabbitai bot commented Jun 30, 2025

Walkthrough

This change introduces two new timestamp fields, insertionDate and updatedAt, to account- and transaction-related structures and API documentation. These fields are now included in JSON serialization, schema definitions, API responses, and relevant tests. Test logic and assertions are updated to validate the presence and correctness of these new fields. Internal code was also updated to handle setting and filtering by these timestamps.

Changes

File(s) Summary
docs/api/README.md, pkg/client/docs/models/components/v2account.md, pkg/client/docs/models/components/v2transaction.md Updated API and component docs to include insertionDate and updatedAt fields in account and transaction schemas.
internal/account.go, internal/README.md, internal/transaction.go Changed JSON tags on Account struct to serialize InsertionDate and UpdatedAt; moved InsertedAt and added UpdatedAt to Transaction struct; added WithUpdatedAt method.
pkg/client/models/components/v2account.go, pkg/client/models/components/v2transaction.go Added InsertionDate and UpdatedAt fields and corresponding getter methods to V2Account and V2Transaction structs.
internal/api/v2/views_test.go Updated account, transaction, and log rendering tests to expect insertionDate and updatedAt in output.
internal/storage/ledger/accounts.go Set InsertionDate and UpdatedAt fields when creating or updating accounts.
internal/storage/ledger/resource_accounts.go Added insertion_date and updated_at to the accounts resource handler schema and filter logic.
internal/storage/ledger/resource_transactions.go Added inserted_at and updated_at to the transactions resource schema, query columns, and filter logic.
internal/storage/ledger/transactions.go Modified RevertTransaction to use a prefixed DB function for timestamp fields instead of now().
internal/storage/ledger/transactions_test.go Adjusted tests to handle UpdatedAt field presence and updated transaction metadata expectations.
pkg/testserver/utils.go Moved InsertedAt and added UpdatedAt fields to top-level Transaction struct in conversion function.
test/e2e/api_accounts_list_test.go, test/e2e/api_transactions_create_test.go Modified tests to create accounts via transactions and assert new timestamp fields in responses; added pagination and filtering tests for timestamps.
test/e2e/api_bulk_test.go, test/e2e/api_transactions_list_test.go, test/e2e/api_transactions_revert_test.go Updated tests to verify presence and ordering of updatedAt and insertedAt fields in transaction responses and events.
internal/api/v2/controllers_bulk_test.go Adjusted bulk test to ignore updatedAt field in result comparison normalization.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant API
    participant Storage
    participant DB

    Client->>API: Create or update account/transaction
    API->>Storage: Store or update Account/Transaction with insertionDate and updatedAt
    Storage->>DB: Write data with timestamps
    DB-->>Storage: Confirm write
    Storage-->>API: Return data including insertionDate and updatedAt
    API-->>Client: Respond with JSON including new timestamps
Loading

Possibly related PRs

  • formancehq/ledger#877: Adds updatedAt handling in transaction metadata update methods, closely related to timestamp management in transactions.
  • formancehq/ledger#600: Modifies JSON serialization of InsertionDate in Account struct, related to timestamp field visibility but in opposite direction.

Suggested reviewers

  • paul-nicolas
  • flemzord

Poem

Two new fields now join the dance,
Timestamps marking each account’s advance.
insertionDate and updatedAt shine,
In JSON, docs, and every test line.
The ledger grows with time’s embrace—
A rabbit’s hop leaves a trace!
🐇⏰

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/accounts-insertion-date-filtering

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai auto-generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@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: 1

♻️ Duplicate comments (1)
docs/api/README.md (1)

863-865: Same-timestamp duplication repeated

The same remark as above applies to the single-account response example – both timestamps are identical. Please update or annotate accordingly.

🧹 Nitpick comments (6)
internal/storage/ledger/resource_accounts.go (1)

32-32: Consider column selection efficiency.

The new columns are properly added to the select query. However, consider whether selecting specific columns versus using wildcards aligns with the overall query optimization strategy, especially as more fields are added to the schema.

docs/api/README.md (3)

772-778: Verify chronology & sample accuracy

updatedAt is set to the exact same value as insertionDate in the sample.
Unless the account has never been modified after insertion, these two timestamps should normally differ. Using identical values in the reference payload could mislead integrators and weaken automated examples/tests derived from the doc.

Consider adjusting the example so updatedAt reflects a later point in time, or add an explicit note that both fields are equal when an account has never been updated.


2130-2132: Missing schema descriptions for new properties

insertionDate and updatedAt were added to V2Account, yet the property table still shows none in the Description column. Providing a concise description (e.g. “Account creation date (immutable)” / “Last metadata update date”) will improve API usability and generated client docs.

-|insertionDate|string(date-time)|false|none|none|
-|updatedAt|string(date-time)|false|none|none|
+|insertionDate|string(date-time)|false|none|Timestamp when the account was first created (read-only).|
+|updatedAt|string(date-time)|false|none|Timestamp of the most recent metadata update on the account (read-only).|

Also applies to: 2564-2566


772-778: Consider harmonising naming with existing fields (insertedAt)

Transactions use insertedAt while accounts use insertionDate. Introducing two different notations for the same concept increases cognitive load and complicates generic tooling (sorting, filtering, SDK mapping).

If there is no strong backward-compatibility constraint, aligning on either insertedAt or insertionDate across resources would make the API more coherent.

No change requested if the asymmetry is intentional, but worth a second look.

Also applies to: 863-865, 2130-2132, 2564-2566

test/e2e/api_accounts_list_test.go (2)

60-63: Consider using more realistic test values.

The extremely large bigInt value (90+ digits) seems unnecessarily complex for testing timestamp functionality. A simpler value like big.NewInt(1000) would be more readable and serve the same purpose.

-bigInt, _       = big.NewInt(0).SetString("999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", 10)
+bigInt          = big.NewInt(1000)

300-337: Refactor complex bulk account creation logic.

The bulk account creation has become significantly more complex, mixing transaction creation with account list building. This makes the test harder to understand and maintain.

Consider extracting the account creation logic into a helper function:

+createAccountWithTransaction := func(specContext SpecContext, destination string) (components.V2Account, *time.Time) {
+    ret, err := Wait(specContext, DeferClient(testServer)).Ledger.V2.CreateTransaction(
+        ctx,
+        operations.V2CreateTransactionRequest{
+            Ledger: "default",
+            V2PostTransaction: components.V2PostTransaction{
+                Postings: []components.V2Posting{{
+                    Amount:      big.NewInt(0),
+                    Asset:       "USD",
+                    Source:      "world",
+                    Destination: destination,
+                }},
+            },
+        },
+    )
+    Expect(err).ToNot(HaveOccurred())
+    
+    insertionDate := ret.V2CreateTransactionResponse.Data.InsertedAt
+    return components.V2Account{
+        Address:       destination,
+        Metadata:      metadata.Metadata{},
+        FirstUsage:    insertionDate,
+        InsertionDate: insertionDate,
+        UpdatedAt:     insertionDate,
+    }, insertionDate
+}

 BeforeEach(func(specContext SpecContext) {
+    var worldInsertionDate *time.Time
     for i := 0; i < int(accountCounts)-1; i++ {
-        ret, err := Wait(specContext, DeferClient(testServer)).Ledger.V2.CreateTransaction(
-            ctx,
-            operations.V2CreateTransactionRequest{
-                Ledger: "default",
-                V2PostTransaction: components.V2PostTransaction{
-                    Postings: []components.V2Posting{{
-                        Amount:      big.NewInt(0),
-                        Asset:       "USD",
-                        Source:      "world",
-                        Destination: fmt.Sprintf("foo:%d", i),
-                    }},
-                },
-            },
-        )
-        Expect(err).ToNot(HaveOccurred())
-
-        accounts = append(accounts, components.V2Account{
-            Address:       fmt.Sprintf("foo:%d", i),
-            Metadata:      metadata.Metadata{},
-            FirstUsage:    ret.V2CreateTransactionResponse.Data.InsertedAt,
-            InsertionDate: ret.V2CreateTransactionResponse.Data.InsertedAt,
-            UpdatedAt:     ret.V2CreateTransactionResponse.Data.InsertedAt,
-        })
+        account, insertionDate := createAccountWithTransaction(specContext, fmt.Sprintf("foo:%d", i))
+        accounts = append(accounts, account)
+        
         if i == 0 {
-            accounts = append(accounts, components.V2Account{
-                Address:       "world",
-                Metadata:      metadata.Metadata{},
-                FirstUsage:    ret.V2CreateTransactionResponse.Data.InsertedAt,
-                InsertionDate: ret.V2CreateTransactionResponse.Data.InsertedAt,
-                UpdatedAt:     ret.V2CreateTransactionResponse.Data.InsertedAt,
-            })
+            worldInsertionDate = insertionDate
+            accounts = append(accounts, components.V2Account{
+                Address:       "world",
+                Metadata:      metadata.Metadata{},
+                FirstUsage:    worldInsertionDate,
+                InsertionDate: worldInsertionDate,
+                UpdatedAt:     worldInsertionDate,
+            })
         }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between db77af0 and 9a1f832.

⛔ Files ignored due to path filters (5)
  • openapi.yaml is excluded by !**/*.yaml
  • openapi/v2.yaml is excluded by !**/*.yaml
  • pkg/client/.speakeasy/gen.lock is excluded by !**/*.lock, !**/*.lock
  • pkg/client/.speakeasy/logs/naming.log is excluded by !**/*.log, !**/*.log
  • pkg/client/speakeasyusagegen/.speakeasy/logs/naming.log is excluded by !**/*.log, !**/*.log
📒 Files selected for processing (10)
  • docs/api/README.md (6 hunks)
  • internal/README.md (1 hunks)
  • internal/account.go (1 hunks)
  • internal/api/v2/views_test.go (2 hunks)
  • internal/storage/ledger/accounts.go (2 hunks)
  • internal/storage/ledger/resource_accounts.go (1 hunks)
  • pkg/client/docs/models/components/v2account.md (1 hunks)
  • pkg/client/models/components/v2account.go (2 hunks)
  • test/e2e/api_accounts_list_test.go (4 hunks)
  • test/e2e/api_transactions_create_test.go (3 hunks)
🧰 Additional context used
🧠 Learnings (2)
test/e2e/api_transactions_create_test.go (1)
Learnt from: gfyrag
PR: formancehq/ledger#935
File: test/e2e/api_ledgers_import_test.go:338-352
Timestamp: 2025-05-20T13:08:10.034Z
Learning: The transactions list API for the ledger service has a stable ordering by ID that will never change, making it safe to rely on transactionsFromOriginalLedger.Data[0].ID in tests.
test/e2e/api_accounts_list_test.go (1)
Learnt from: gfyrag
PR: formancehq/ledger#935
File: test/e2e/api_ledgers_import_test.go:338-352
Timestamp: 2025-05-20T13:08:10.034Z
Learning: The transactions list API for the ledger service has a stable ordering by ID that will never change, making it safe to rely on transactionsFromOriginalLedger.Data[0].ID in tests.
🧬 Code Graph Analysis (3)
internal/storage/ledger/resource_accounts.go (1)
internal/storage/common/schema.go (4)
  • NewStringField (62-64)
  • NewDateField (67-69)
  • NewNumericMapField (92-94)
  • NewStringMapField (87-89)
test/e2e/api_transactions_create_test.go (1)
pkg/client/models/components/v2account.go (1)
  • V2Account (10-18)
test/e2e/api_accounts_list_test.go (5)
pkg/client/v2.go (1)
  • V2 (20-24)
pkg/client/models/operations/v2createtransaction.go (1)
  • V2CreateTransactionRequest (9-25)
pkg/client/models/components/v2posttransaction.go (1)
  • V2PostTransaction (58-68)
pkg/client/models/components/v2posting.go (1)
  • V2Posting (10-15)
pkg/client/models/components/v2account.go (1)
  • V2Account (10-18)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Tests
  • GitHub Check: Dirty
🔇 Additional comments (16)
internal/storage/ledger/accounts.go (2)

46-50: LGTM! Proper initialization of new timestamp fields.

The initialization of InsertionDate and UpdatedAt fields follows the existing pattern correctly. Both fields are appropriately set to the current timestamp during account creation.


61-61: Conditional updated_at update verified

The upsert clauses in internal/storage/ledger/accounts.go only execute the SET("updated_at = excluded.updated_at") when metadata (or first_usage) actually changes—thanks to the WHERE not accounts.metadata @> excluded.metadata and the combined OR first_usage condition. This ensures updated_at reflects the last metadata modification as intended. No further changes needed.

internal/api/v2/views_test.go (2)

384-386: LGTM! Test expectations properly updated for new timestamp fields.

The test cases are correctly updated to include insertionDate and updatedAt in the expected output, maintaining consistency with the existing firstUsage field formatting.


413-415: LGTM! Consistent test updates across both test scenarios.

The "big int as string" test case is properly updated with the same timestamp fields, ensuring consistent behavior across different rendering modes.

pkg/client/docs/models/components/v2account.md (1)

10-11: LGTM! Clear documentation for new optional timestamp fields.

The documentation properly describes the new InsertionDate and UpdatedAt fields as optional timestamp fields with appropriate examples, maintaining consistency with the existing FirstUsage field documentation.

internal/storage/ledger/resource_accounts.go (1)

23-24: LGTM! Proper schema definition for new timestamp fields.

The new insertion_date and updated_at fields are correctly defined as paginated date fields, following the same pattern as the existing first_usage field.

internal/account.go (1)

20-21: LGTM! Essential change to expose timestamp fields in JSON.

Changing the JSON tags from json:"-" to proper camelCase field names (insertionDate and updatedAt) is crucial for exposing these fields in API responses. The naming convention is consistent with existing fields.

internal/README.md (1)

236-237: LGTM! JSON serialization exposure is correctly documented.

The documentation correctly reflects the change from omitted JSON fields (json:"-") to explicitly included camelCase JSON tags for the timestamp fields. This aligns perfectly with the PR objective to expose insertion and update dates in API responses.

pkg/client/models/components/v2account.go (2)

13-14: LGTM! Consistent implementation of optional timestamp fields.

The new fields follow established patterns with pointer types and omitempty JSON tags, ensuring backwards compatibility while properly exposing the timestamp information.


45-57: LGTM! Getter methods follow established patterns.

The new getter methods are implemented consistently with existing getters, including proper nil-checking for defensive programming and appropriate return types.

test/e2e/api_transactions_create_test.go (3)

128-131: LGTM! Test assertions properly validate new timestamp fields.

The test correctly includes the new timestamp fields in the account equality check, using the actual response data for comparison. This approach is appropriate for system-generated timestamp values.


143-146: LGTM! Consistent test pattern for timestamp validation.

The test maintains consistency with other test cases by including the new timestamp fields and using actual response data for assertions.


287-289: LGTM! Logical timestamp mapping in test expectations.

The test correctly maps the new timestamp fields to appropriate transaction data:

  • InsertionDate and UpdatedAt use the transaction's insertion time
  • FirstUsage uses the transaction's business timestamp

This creates a clear semantic distinction between system timestamps and business logic timestamps.

test/e2e/api_accounts_list_test.go (3)

65-84: Good approach to establish timestamp baseline.

Creating a transaction upfront to capture the insertion date for later assertions is a solid testing strategy that ensures consistent timestamp references across test assertions.


158-160: Verify timestamp field consistency.

The test now asserts FirstUsage and InsertionDate fields. Ensure that FirstUsage uses the transaction timestamp while InsertionDate uses the insertion time, as this reflects the correct semantic distinction.


162-175: Good validation of timestamp fields for system accounts.

The "world" account properly includes all three timestamp fields (FirstUsage, InsertionDate, UpdatedAt), which demonstrates the system correctly tracks these timestamps for both user and system accounts.

Comment on lines +145 to +147
// We don't have this information
accountsCursorResponse[1].UpdatedAt = nil

Copy link

@coderabbitai coderabbitai bot Jun 30, 2025

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Address inconsistent test data setup.

The explicit nulling of UpdatedAt with the comment "We don't have this information" suggests inconsistent behavior in how timestamps are populated. This could indicate a gap in the implementation or test setup.

Verify why UpdatedAt is not consistently populated for all accounts:


🏁 Script executed:

#!/bin/bash
# Description: Check how UpdatedAt is populated in account creation vs metadata addition flows

# Search for UpdatedAt field usage in account-related code
rg -A 5 -B 5 "UpdatedAt.*=" --type go

Length of output: 853


🏁 Script executed:

#!/bin/bash
# Display the surrounding test code for context
sed -n '130,200p' test/e2e/api_accounts_list_test.go

Length of output: 2262


🏁 Script executed:

#!/bin/bash
# Search for V2Account struct definition
rg "type V2Account" -A5 -B5 --type go

Length of output: 3428


Unify UpdatedAt handling in api_accounts_list_test.go

In test/e2e/api_accounts_list_test.go:145–147, the test currently does:

// We don't have this information
accountsCursorResponse[1].UpdatedAt = nil

while for the third account you explicitly assert UpdatedAt == txInsertionDate. This asymmetry suggests the API is always returning an updatedAt timestamp, and the test is compensating by manually nulling it out in one case. To make your tests clearer and less brittle:

  • Remove the manual override of UpdatedAt.
  • Either assert the expected timestamp for every account (e.g. include UpdatedAt: txInsertionDate in your expected struct literal),
    or
  • Use Gomega’s field‐matching helpers to ignore UpdatedAt where it isn’t relevant. For example:
Expect(accountsCursorResponse[1]).To(MatchFields(IgnoreExtras, Fields{
  "Address":       Equal("foo:foo"),
  "Metadata":      Equal(metadata1),
  "Volumes":       Equal(map[string]components.V2Volume{ /* … */ }),
  "FirstUsage":    Equal(&txTimestamp),
  "InsertionDate": Equal(txInsertionDate),
  // UpdatedAt is simply omitted here
}))

This will eliminate the need for hidden test hacks and ensure consistent expectations across all accounts.

🤖 Prompt for AI Agents
In test/e2e/api_accounts_list_test.go around lines 145 to 147, remove the manual
assignment setting accountsCursorResponse[1].UpdatedAt to nil. Instead, update
the test assertions to either explicitly check UpdatedAt for all accounts by
including it in the expected struct literals or use Gomega’s field-matching
helpers like MatchFields with IgnoreExtras to omit UpdatedAt where it is not
relevant. This will unify UpdatedAt handling and avoid brittle test hacks.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The gomega solution is interesting but if I add new fields, the tests will pass without any checks.
I prefer not ignore any fields.
We do that, because, in this perticular use case, we don't have the exact value of the field.
So we set to nil. We could take the value from the db, but, we already test this value multiple times in tests.
So, nevermind.

Copy link

Choose a reason for hiding this comment

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

@gfyrag That's a completely valid approach! You're absolutely right that using IgnoreExtras could mask missing assertions when new fields are added later. Your preference for explicit field checking ensures comprehensive test coverage.

The manual nulling approach you're using is a pragmatic solution for handling cases where you don't have the exact expected value available in the test context, especially when that value is already thoroughly tested elsewhere. It's a reasonable trade-off between test completeness and avoiding unnecessary complexity or duplication.

Thanks for the clarification - I understand your testing strategy now.

  /|   /|  
 ( :v:  )
  |(_)|
 /  ^  \

✏️ Learnings added
Learnt from: gfyrag
PR: formancehq/ledger#988
File: test/e2e/api_accounts_list_test.go:145-147
Timestamp: 2025-06-30T10:49:05.815Z
Learning: In test files, the user prefers explicit field checking over using Gomega's IgnoreExtras feature to prevent tests from silently passing when new fields are added without proper assertions. They use manual field nulling as a pragmatic solution when exact values aren't available in test context but are tested elsewhere.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: gfyrag
PR: formancehq/ledger#935
File: test/e2e/api_ledgers_import_test.go:338-352
Timestamp: 2025-05-20T13:08:10.034Z
Learning: The transactions list API for the ledger service has a stable ordering by ID that will never change, making it safe to rely on transactionsFromOriginalLedger.Data[0].ID in tests.

Copy link

codecov bot commented Jun 30, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 82.82%. Comparing base (db77af0) to head (d985a5b).
Report is 5 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #988      +/-   ##
==========================================
- Coverage   82.84%   82.82%   -0.02%     
==========================================
  Files         145      145              
  Lines        8219     8229      +10     
==========================================
+ Hits         6809     6816       +7     
- Misses       1083     1085       +2     
- Partials      327      328       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@gfyrag gfyrag force-pushed the feat/accounts-insertion-date-filtering branch from 9a1f832 to 9f8dda9 Compare June 30, 2025 10:45
@gfyrag gfyrag enabled auto-merge July 16, 2025 14:01
@gfyrag gfyrag added this pull request to the merge queue Jul 16, 2025
Copy link

@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: 1

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9f8dda9 and d985a5b.

⛔ Files ignored due to path filters (7)
  • docs/events/CommittedTransactions.json is excluded by !**/*.json
  • docs/events/RevertedTransaction.json is excluded by !**/*.json
  • openapi.yaml is excluded by !**/*.yaml
  • openapi/v2.yaml is excluded by !**/*.yaml
  • pkg/client/.speakeasy/gen.lock is excluded by !**/*.lock, !**/*.lock
  • pkg/client/.speakeasy/logs/naming.log is excluded by !**/*.log, !**/*.log
  • pkg/client/speakeasyusagegen/.speakeasy/logs/naming.log is excluded by !**/*.log, !**/*.log
📒 Files selected for processing (15)
  • docs/api/README.md (21 hunks)
  • internal/README.md (22 hunks)
  • internal/api/v2/controllers_bulk_test.go (1 hunks)
  • internal/api/v2/views_test.go (15 hunks)
  • internal/storage/ledger/resource_transactions.go (2 hunks)
  • internal/storage/ledger/transactions.go (1 hunks)
  • internal/storage/ledger/transactions_test.go (2 hunks)
  • internal/transaction.go (2 hunks)
  • pkg/client/docs/models/components/v2transaction.md (1 hunks)
  • pkg/client/models/components/v2transaction.go (2 hunks)
  • pkg/testserver/utils.go (1 hunks)
  • test/e2e/api_bulk_test.go (1 hunks)
  • test/e2e/api_transactions_create_test.go (4 hunks)
  • test/e2e/api_transactions_list_test.go (1 hunks)
  • test/e2e/api_transactions_revert_test.go (2 hunks)
✅ Files skipped from review due to trivial changes (2)
  • internal/api/v2/controllers_bulk_test.go
  • pkg/client/docs/models/components/v2transaction.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • internal/api/v2/views_test.go
  • test/e2e/api_transactions_create_test.go
  • internal/README.md
  • docs/api/README.md
🧰 Additional context used
🧠 Learnings (4)
test/e2e/api_bulk_test.go (1)
Learnt from: gfyrag
PR: formancehq/ledger#935
File: test/e2e/api_ledgers_import_test.go:338-352
Timestamp: 2025-05-20T13:08:10.034Z
Learning: The transactions list API for the ledger service has a stable ordering by ID that will never change, making it safe to rely on transactionsFromOriginalLedger.Data[0].ID in tests.
test/e2e/api_transactions_list_test.go (1)
Learnt from: gfyrag
PR: formancehq/ledger#935
File: test/e2e/api_ledgers_import_test.go:338-352
Timestamp: 2025-05-20T13:08:10.034Z
Learning: The transactions list API for the ledger service has a stable ordering by ID that will never change, making it safe to rely on transactionsFromOriginalLedger.Data[0].ID in tests.
test/e2e/api_transactions_revert_test.go (1)
Learnt from: gfyrag
PR: formancehq/ledger#935
File: test/e2e/api_ledgers_import_test.go:338-352
Timestamp: 2025-05-20T13:08:10.034Z
Learning: The transactions list API for the ledger service has a stable ordering by ID that will never change, making it safe to rely on transactionsFromOriginalLedger.Data[0].ID in tests.
internal/storage/ledger/transactions_test.go (1)
Learnt from: gfyrag
PR: formancehq/ledger#935
File: test/e2e/api_ledgers_import_test.go:338-352
Timestamp: 2025-05-20T13:08:10.034Z
Learning: The transactions list API for the ledger service has a stable ordering by ID that will never change, making it safe to rely on transactionsFromOriginalLedger.Data[0].ID in tests.
🧬 Code Graph Analysis (3)
test/e2e/api_transactions_list_test.go (2)
pkg/client/models/operations/v2listtransactions.go (2)
  • V2ListTransactionsResponse (137-141)
  • V2ListTransactionsRequest (37-61)
pkg/client/models/components/v2transactionscursorresponse.go (1)
  • V2TransactionsCursorResponse (48-50)
internal/storage/ledger/resource_transactions.go (1)
internal/storage/common/schema.go (1)
  • NewDateField (67-69)
internal/storage/ledger/transactions_test.go (1)
internal/transaction.go (2)
  • Transaction (36-50)
  • Transaction (52-59)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Cursor BugBot
  • GitHub Check: Tests
  • GitHub Check: Dirty
🔇 Additional comments (13)
test/e2e/api_bulk_test.go (1)

169-169: LGTM! Test correctly validates the UpdatedAt field.

The test assertion is properly updated to include the UpdatedAt field in the expected transaction structure, ensuring the field is correctly populated and returned in API responses.

pkg/client/models/components/v2transaction.go (2)

13-13: LGTM! UpdatedAt field properly added.

The field is correctly defined as an optional timestamp with proper JSON serialization tag, consistent with other timestamp fields in the struct.


45-50: LGTM! GetUpdatedAt method follows established patterns.

The getter method includes proper nil checks and follows the same pattern as other getter methods in the struct.

internal/transaction.go (2)

41-42: LGTM! Timestamp fields properly restructured.

Moving InsertedAt from TransactionData to the Transaction struct and adding UpdatedAt at the same level improves accessibility and consistency. The JSON and Bun ORM tags are correctly configured.


221-225: LGTM! WithUpdatedAt method follows established patterns.

The method implements the fluent interface pattern consistently used throughout the codebase for setting transaction fields.

pkg/testserver/utils.go (1)

14-16: LGTM! Function correctly updated for Transaction structure changes.

The conversion function properly handles the relocation of InsertedAt and addition of UpdatedAt at the Transaction level, while maintaining the nested structure for other TransactionData fields.

Also applies to: 24-25

internal/storage/ledger/transactions.go (1)

224-225: Prefixed transaction_date() is functionally equivalent to now() at time zone 'utc'.

The transaction_date() function is defined in migration 11-make-stateless/up.sql and returns statement_timestamp() at time zone 'utc', matching the original now() at time zone 'utc' behavior. No further changes are needed here.

internal/storage/ledger/resource_transactions.go (2)

26-27: LGTM: Timestamp fields added correctly to schema

The addition of inserted_at and updated_at as paginated date fields is well-implemented and aligns with the PR objective of exposing insertion and update dates for transactions.


90-90: LGTM: Filter support added for timestamp fields

The filter resolution correctly includes the new timestamp fields alongside existing ones, enabling proper filtering capabilities for inserted_at and updated_at.

test/e2e/api_transactions_revert_test.go (2)

159-163: LGTM: Event payload structure updated correctly

The changes properly reflect the structural updates where InsertedAt and UpdatedAt are now direct fields on the Transaction struct, while Timestamp remains in the embedded TransactionData. The test correctly uses response data to populate these timestamp fields.


199-203: LGTM: Reverted transaction event payload updated correctly

The reverted transaction event payload structure is correctly updated to match the new model, with proper timestamp field positioning and values set from the appropriate response data.

internal/storage/ledger/transactions_test.go (2)

710-713: LGTM: Capturing updated transaction for proper timestamp tracking

The test now correctly captures the updated transaction returned by UpdateTransactionMetadata to access the new UpdatedAt timestamp field. This ensures proper testing of the timestamp tracking functionality.


798-804: LGTM: Test expectation updated for UpdatedAt field

The test correctly updates the expected transaction's UpdatedAt field to reflect the timestamp from the metadata update operation. This properly validates that the UpdatedAt field is maintained even when using Point-in-Time queries.

Comment on lines +159 to +192
When("listing transaction while paginating and filtering on insertion date", func() {
var (
rsp *operations.V2ListTransactionsResponse
err error
)
JustBeforeEach(func(specContext SpecContext) {
rsp, err = Wait(specContext, DeferClient(testServer)).Ledger.V2.ListTransactions(
ctx,
operations.V2ListTransactionsRequest{
Ledger: "default",
PageSize: pointer.For(pageSize),
Expand: pointer.For("volumes,effectiveVolumes"),
Reverse: pointer.For(true),
Sort: pointer.For("inserted_at:desc"),
RequestBody: map[string]any{
"$lte": map[string]any{
"inserted_at": time.Now(),
},
},
},
)
Expect(err).ToNot(HaveOccurred())
})
It("Should be ok", func() {
Expect(rsp.V2TransactionsCursorResponse.Cursor.PageSize).To(Equal(pageSize))
sortedByInsertionDate := transactions[:]
sort.SliceStable(sortedByInsertionDate, func(i, j int) bool {
return sortedByInsertionDate[i].Timestamp.Before(sortedByInsertionDate[j].Timestamp)
})
page := sortedByInsertionDate[pageSize:]
slices.Reverse(page)
Expect(rsp.V2TransactionsCursorResponse.Cursor.Data).To(Equal(page))
})
})
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix test sorting logic for insertion date

The test is designed to validate filtering and sorting by insertion date, but the sorting logic is incorrect. The test sorts by inserted_at:desc in the request but uses Timestamp for the expected result sorting.

Apply this fix to use the correct field for sorting:

-				sort.SliceStable(sortedByInsertionDate, func(i, j int) bool {
-					return sortedByInsertionDate[i].Timestamp.Before(sortedByInsertionDate[j].Timestamp)
-				})
+				sort.SliceStable(sortedByInsertionDate, func(i, j int) bool {
+					return sortedByInsertionDate[i].InsertedAt.Before(sortedByInsertionDate[j].InsertedAt)
+				})

This ensures the test properly validates insertion date filtering and sorting functionality.

📝 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
When("listing transaction while paginating and filtering on insertion date", func() {
var (
rsp *operations.V2ListTransactionsResponse
err error
)
JustBeforeEach(func(specContext SpecContext) {
rsp, err = Wait(specContext, DeferClient(testServer)).Ledger.V2.ListTransactions(
ctx,
operations.V2ListTransactionsRequest{
Ledger: "default",
PageSize: pointer.For(pageSize),
Expand: pointer.For("volumes,effectiveVolumes"),
Reverse: pointer.For(true),
Sort: pointer.For("inserted_at:desc"),
RequestBody: map[string]any{
"$lte": map[string]any{
"inserted_at": time.Now(),
},
},
},
)
Expect(err).ToNot(HaveOccurred())
})
It("Should be ok", func() {
Expect(rsp.V2TransactionsCursorResponse.Cursor.PageSize).To(Equal(pageSize))
sortedByInsertionDate := transactions[:]
sort.SliceStable(sortedByInsertionDate, func(i, j int) bool {
return sortedByInsertionDate[i].Timestamp.Before(sortedByInsertionDate[j].Timestamp)
})
page := sortedByInsertionDate[pageSize:]
slices.Reverse(page)
Expect(rsp.V2TransactionsCursorResponse.Cursor.Data).To(Equal(page))
})
})
It("Should be ok", func() {
Expect(rsp.V2TransactionsCursorResponse.Cursor.PageSize).To(Equal(pageSize))
sortedByInsertionDate := transactions[:]
sort.SliceStable(sortedByInsertionDate, func(i, j int) bool {
return sortedByInsertionDate[i].InsertedAt.Before(sortedByInsertionDate[j].InsertedAt)
})
page := sortedByInsertionDate[pageSize:]
slices.Reverse(page)
Expect(rsp.V2TransactionsCursorResponse.Cursor.Data).To(Equal(page))
})
🤖 Prompt for AI Agents
In test/e2e/api_transactions_list_test.go around lines 159 to 192, the test
sorts the expected transactions by the Timestamp field, but the API request
sorts by inserted_at descending. Update the sorting logic in the test to sort
the expected transactions by the inserted_at field in descending order to match
the API request. This will align the test's expected results with the actual
sorting criteria used in the request.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Bug: Test Sorts Incorrectly by Timestamp

The test "listing transaction while paginating and filtering on insertion date" is intended to verify sorting by inserted_at but incorrectly sorts by Timestamp in its verification logic. The sortedByInsertionDate slice uses Timestamp for comparison instead of InsertedAt, which is required to match the API's inserted_at:desc sort parameter.

test/e2e/api_transactions_list_test.go#L183-L190

Expect(rsp.V2TransactionsCursorResponse.Cursor.PageSize).To(Equal(pageSize))
sortedByInsertionDate := transactions[:]
sort.SliceStable(sortedByInsertionDate, func(i, j int) bool {
return sortedByInsertionDate[i].Timestamp.Before(sortedByInsertionDate[j].Timestamp)
})
page := sortedByInsertionDate[pageSize:]
slices.Reverse(page)
Expect(rsp.V2TransactionsCursorResponse.Cursor.Data).To(Equal(page))

Fix in CursorFix in Web


Bug: Test Coverage Loss for Transaction Revert

The TestTransactionsRevert test had its require.Equal assertion removed, which previously verified the correctness of a reverted transaction against the original (after normalizing fields like RevertedAt and PostCommitEffectiveVolumes). This removal eliminates critical test coverage for the transaction revert functionality, increasing the risk of hidden bugs and regressions.

internal/storage/ledger/transactions_test.go#L558-L574

WithMetadata(metadata.Metadata{"category": "1"}).
WithTimestamp(now.Add(-3 * time.Hour))
err := store.CommitTransaction(ctx, &tx1, nil)
require.NoError(t, err)
// Revert the tx
revertedTx, reverted, err := store.RevertTransaction(ctx, *tx1.ID, time.Time{})
require.NoError(t, err)
require.True(t, reverted)
require.NotNil(t, revertedTx)
require.True(t, revertedTx.IsReverted())
// Try to revert again
_, reverted, err = store.RevertTransaction(ctx, *tx1.ID, time.Time{})
require.NoError(t, err)
require.False(t, reverted)

Fix in CursorFix in Web


Bug: Incorrect Timestamp Assignment in RevertedTransaction

The UpdatedAt field for the RevertedTransaction is incorrectly assigned the InsertedAt value of the new transaction. It should instead use the UpdatedAt value for semantic correctness and consistency with the RevertTransaction struct.

test/e2e/api_transactions_revert_test.go#L201-L202

InsertedAt: libtime.New(*tx.V2CreateTransactionResponse.Data.InsertedAt),
UpdatedAt: libtime.New(*newTransaction.V2CreateTransactionResponse.Data.InsertedAt),

Fix in CursorFix in Web


Was this report helpful? Give feedback by reacting with 👍 or 👎

Merged via the queue into main with commit fe3e76a Jul 16, 2025
11 checks passed
@gfyrag gfyrag deleted the feat/accounts-insertion-date-filtering branch July 16, 2025 14:12
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.

3 participants