Skip to content

RHCLOUD-45005: Use LockId and LockToken tiny types in tuple commands#1331

Merged
snehagunta merged 2 commits intoproject-kessel:mainfrom
snehagunta:RHCLOUD-45005-tiny-types-lock-id-token
May 1, 2026
Merged

RHCLOUD-45005: Use LockId and LockToken tiny types in tuple commands#1331
snehagunta merged 2 commits intoproject-kessel:mainfrom
snehagunta:RHCLOUD-45005-tiny-types-lock-id-token

Conversation

@snehagunta
Copy link
Copy Markdown
Contributor

@snehagunta snehagunta commented Apr 30, 2026

Summary

  • Replace string with model.LockId and model.LockToken in AcquireLockCommand, FencingCheck, and AcquireLockResult
  • Validate at the service boundary via NewLockId/NewLockToken; remove redundant DeserializeLockId/DeserializeLockToken calls in the usecase layer
  • Invalid (empty) lock IDs/tokens now fail conversion with a clear error instead of reaching the usecase

Stack

3/4 in the tiny-types series. Depends on #1330 (merge that first).

Test plan

  • go build ./... passes
  • go test ./internal/... — all 36 packages pass

Made with Cursor

Summary by CodeRabbit

  • Refactor

    • Strengthened lock management with improved type safety for lock identifiers and tokens.
    • Centralized validation logic for lock-related operations to ensure consistency.
  • Bug Fixes

    • Enhanced error detection for invalid lock inputs and configurations.
    • Lock validation now occurs earlier in the request processing lifecycle.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 30, 2026

📝 Walkthrough

Walkthrough

The changes introduce strongly-typed lock identifiers and tokens throughout the locking system by replacing string fields with model.LockId and model.LockToken types. Command/result structures, use case logic, service layer validation, and corresponding tests are updated consistently to use proper type constructors.

Changes

Cohort / File(s) Summary
Type Definitions
internal/biz/usecase/tuples/commands.go
Updates struct field types from string to model.LockId and model.LockToken for AcquireLockCommand.LockId, FencingCheck.LockId, FencingCheck.LockToken, and AcquireLockResult.LockToken.
Use Case Layer
internal/biz/usecase/tuples/tuple_crud_usecase.go, internal/biz/usecase/tuples/tuple_crud_usecase_test.go
Implements typed lock identifier handling in use case methods by passing lock identifiers directly to constructors, removing deserialization steps, and updating tests to pass model.DeserializeLockId() values instead of raw strings.
Service Layer
internal/service/tuples/tuples.go, internal/service/tuples/tuples_test.go
Adds validation helpers (toAcquireLockCommand, fencingCheckFromProto) to convert and validate proto lock values using domain constructors; refactors fencing-check mapping and serializes lock tokens explicitly; updates tests to use strongly-typed values for comparisons.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers the key changes and rationale, but lacks required template sections like ticket reference, acceptance criteria confirmation, and deployment/testing checklists. Add ticket reference (Fixes #), completion of checklist items (acceptance criteria, 4-eye principle, testing, automation/SOP, deployment, security, and public API review), and confirm Ticket state.
Docstring Coverage ⚠️ Warning Docstring coverage is 18.75% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: replacing string types with LockId and LockToken tiny types in tuple commands.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

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

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 30, 2026

Codecov Report

❌ Patch coverage is 52.94118% with 16 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/service/tuples/tuples.go 53.33% 9 Missing and 5 partials ⚠️
internal/biz/usecase/tuples/tuple_crud_usecase.go 50.00% 2 Missing ⚠️
Flag Coverage Δ
main 49.72% <52.94%> (+<0.01%) ⬆️
v1beta2 64.95% <52.94%> (-0.03%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
internal/biz/usecase/tuples/tuple_crud_usecase.go 75.00% <50.00%> (-0.52%) ⬇️
internal/service/tuples/tuples.go 71.23% <53.33%> (-3.52%) ⬇️

... and 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Replace raw string with model.LockId and model.LockToken in
AcquireLockCommand, FencingCheck, and AcquireLockResult. Validate
at the service boundary via NewLockId/NewLockToken; remove redundant
DeserializeLockId/DeserializeLockToken calls in the usecase layer.

Made-with: Cursor
@snehagunta snehagunta force-pushed the RHCLOUD-45005-tiny-types-lock-id-token branch from 31dbe28 to 202eb7c Compare May 1, 2026 15:58
@snehagunta
Copy link
Copy Markdown
Contributor Author

Sanity Test Results

Branch: RHCLOUD-45005-tiny-types-lock-id-token

Commit: 202eb7c1
Result: All 16/16 tests PASSED

Full test output
[INFO] Namespace: ephemeral-wex1at
[INFO] Discovering DB credentials...
[INFO] DB credentials discovered (user=DAIW8DjkqXsVDPf6, db=kessel-inventory)
[INFO] Starting API port-forward (localhost:9000 -> svc/kessel-inventory-api:9000)...
[INFO] Starting DB port-forward (localhost:15432 -> svc/kessel-inventory-db:5432)...
[INFO] Waiting for port-forwards to be ready...
Forwarding from 127.0.0.1:9000 -> 9000
Forwarding from [::1]:9000 -> 9000
Forwarding from 127.0.0.1:15432 -> 5432
Forwarding from [::1]:15432 -> 5432
[INFO] Port-forwards ready
[INFO] Running sanity tests...
==============================================
Handling connection for 15432
=== RUN   TestSanity_CheckBulk_MultipleHosts

────────────────────────────────────────────────────────────────────────────────
[1] TestSanity_CheckBulk_MultipleHosts
────────────────────────────────────────────────────────────────────────────────
Handling connection for 9000
    bulk_test.go:38: Check attempt 1: got ALLOWED_FALSE, want ALLOWED_TRUE
    bulk_test.go:38: Check returned ALLOWED_TRUE on attempt 2
    bulk_test.go:40: Check returned ALLOWED_TRUE on attempt 1
    bulk_test.go:104: CheckBulk attempt 1: results not yet consistent, retrying...
    bulk_test.go:104: CheckBulk attempt 2: results not yet consistent, retrying...
    bulk_test.go:104: CheckBulk attempt 3: results not yet consistent, retrying...
    bulk_test.go:104: CheckBulk attempt 4: results not yet consistent, retrying...
    bulk_test.go:104: CheckBulk attempt 5: results not yet consistent, retrying...
    bulk_test.go:104: CheckBulk attempt 6: results not yet consistent, retrying...
    bulk_test.go:104: CheckBulk attempt 7: results not yet consistent, retrying...
    bulk_test.go:98: CheckBulk returned expected results on attempt 8
--- PASS: TestSanity_CheckBulk_MultipleHosts (8.87s)
=== RUN   TestSanity_CheckSelf_ReturnsError

────────────────────────────────────────────────────────────────────────────────
[2] TestSanity_CheckSelf_ReturnsError
────────────────────────────────────────────────────────────────────────────────
Handling connection for 9000
    bulk_test.go:140: CheckSelf error (expected): rpc error: code = PermissionDenied desc = meta authorization denied
--- PASS: TestSanity_CheckSelf_ReturnsError (0.31s)
=== RUN   TestSanity_CheckSelfBulk_ReturnsError

────────────────────────────────────────────────────────────────────────────────
[3] TestSanity_CheckSelfBulk_ReturnsError
────────────────────────────────────────────────────────────────────────────────
Handling connection for 9000
    bulk_test.go:169: CheckSelfBulk error (expected): rpc error: code = PermissionDenied desc = meta authorization denied
--- PASS: TestSanity_CheckSelfBulk_ReturnsError (0.33s)
=== RUN   TestSanity_ReportHost_CheckAllowed

────────────────────────────────────────────────────────────────────────────────
[4] TestSanity_ReportHost_CheckAllowed
────────────────────────────────────────────────────────────────────────────────
Handling connection for 9000
    check_test.go:31: Check attempt 1: got ALLOWED_FALSE, want ALLOWED_TRUE
    check_test.go:31: Check returned ALLOWED_TRUE on attempt 2
--- PASS: TestSanity_ReportHost_CheckAllowed (1.58s)
=== RUN   TestSanity_Check_WrongWorkspace

────────────────────────────────────────────────────────────────────────────────
[5] TestSanity_Check_WrongWorkspace
────────────────────────────────────────────────────────────────────────────────
Handling connection for 9000
    check_test.go:50: Check returned ALLOWED_FALSE on attempt 1
--- PASS: TestSanity_Check_WrongWorkspace (0.34s)
=== RUN   TestSanity_DeleteHost_AccessLost

────────────────────────────────────────────────────────────────────────────────
[6] TestSanity_DeleteHost_AccessLost
────────────────────────────────────────────────────────────────────────────────
Handling connection for 9000
    check_test.go:66: Check attempt 1: got ALLOWED_FALSE, want ALLOWED_TRUE
    check_test.go:66: Check returned ALLOWED_TRUE on attempt 2
    check_test.go:77: Check attempt 1: got ALLOWED_TRUE, want ALLOWED_FALSE
    check_test.go:77: Check attempt 2: got ALLOWED_TRUE, want ALLOWED_FALSE
    check_test.go:77: Check returned ALLOWED_FALSE on attempt 3
--- PASS: TestSanity_DeleteHost_AccessLost (3.49s)
=== RUN   TestSanity_Check_Combinations
=== RUN   TestSanity_Check_Combinations/matching_workspace

────────────────────────────────────────────────────────────────────────────────
[7] TestSanity_Check_Combinations/matching_workspace
────────────────────────────────────────────────────────────────────────────────
Handling connection for 9000
    check_test.go:125: Check returned ALLOWED_TRUE on attempt 1
=== RUN   TestSanity_Check_Combinations/non_matching_workspace

────────────────────────────────────────────────────────────────────────────────
[8] TestSanity_Check_Combinations/non_matching_workspace
────────────────────────────────────────────────────────────────────────────────
Handling connection for 9000
    check_test.go:125: Check returned ALLOWED_FALSE on attempt 1
=== RUN   TestSanity_Check_Combinations/no_instance_in_check_ref

────────────────────────────────────────────────────────────────────────────────
[9] TestSanity_Check_Combinations/no_instance_in_check_ref
────────────────────────────────────────────────────────────────────────────────
Handling connection for 9000
    check_test.go:125: Check attempt 1: got ALLOWED_FALSE, want ALLOWED_TRUE
    check_test.go:125: Check returned ALLOWED_TRUE on attempt 2
=== RUN   TestSanity_Check_Combinations/with_instance_in_check_ref

────────────────────────────────────────────────────────────────────────────────
[10] TestSanity_Check_Combinations/with_instance_in_check_ref
────────────────────────────────────────────────────────────────────────────────
Handling connection for 9000
    check_test.go:125: Check attempt 1: got ALLOWED_FALSE, want ALLOWED_TRUE
    check_test.go:125: Check returned ALLOWED_TRUE on attempt 2
--- PASS: TestSanity_Check_Combinations (3.62s)
    --- PASS: TestSanity_Check_Combinations/matching_workspace (0.40s)
    --- PASS: TestSanity_Check_Combinations/non_matching_workspace (0.37s)
    --- PASS: TestSanity_Check_Combinations/no_instance_in_check_ref (1.42s)
    --- PASS: TestSanity_Check_Combinations/with_instance_in_check_ref (1.42s)
=== RUN   TestSanity_CheckForUpdate_Combinations
=== RUN   TestSanity_CheckForUpdate_Combinations/matching

────────────────────────────────────────────────────────────────────────────────
[11] TestSanity_CheckForUpdate_Combinations/matching
────────────────────────────────────────────────────────────────────────────────
Handling connection for 9000
    checkforupdate_test.go:58: CheckForUpdate attempt 1: got ALLOWED_FALSE, want ALLOWED_TRUE
    checkforupdate_test.go:58: CheckForUpdate returned ALLOWED_TRUE on attempt 2
=== RUN   TestSanity_CheckForUpdate_Combinations/non_matching

────────────────────────────────────────────────────────────────────────────────
[12] TestSanity_CheckForUpdate_Combinations/non_matching
────────────────────────────────────────────────────────────────────────────────
Handling connection for 9000
    checkforupdate_test.go:58: CheckForUpdate returned ALLOWED_FALSE on attempt 1
=== RUN   TestSanity_CheckForUpdate_Combinations/after_delete

────────────────────────────────────────────────────────────────────────────────
[13] TestSanity_CheckForUpdate_Combinations/after_delete
────────────────────────────────────────────────────────────────────────────────
Handling connection for 9000
    checkforupdate_test.go:46: Check attempt 1: got ALLOWED_FALSE, want ALLOWED_TRUE
    checkforupdate_test.go:46: Check returned ALLOWED_TRUE on attempt 2
    checkforupdate_test.go:51: Check attempt 1: got ALLOWED_TRUE, want ALLOWED_FALSE
    checkforupdate_test.go:51: Check attempt 2: got ALLOWED_TRUE, want ALLOWED_FALSE
    checkforupdate_test.go:51: Check attempt 3: got ALLOWED_TRUE, want ALLOWED_FALSE
    checkforupdate_test.go:51: Check attempt 4: got ALLOWED_TRUE, want ALLOWED_FALSE
    checkforupdate_test.go:51: Check attempt 5: got ALLOWED_TRUE, want ALLOWED_FALSE
    checkforupdate_test.go:51: Check attempt 6: got ALLOWED_TRUE, want ALLOWED_FALSE
    checkforupdate_test.go:51: Check attempt 7: got ALLOWED_TRUE, want ALLOWED_FALSE
    checkforupdate_test.go:51: Check returned ALLOWED_FALSE on attempt 8
    checkforupdate_test.go:58: CheckForUpdate returned ALLOWED_FALSE on attempt 1
--- PASS: TestSanity_CheckForUpdate_Combinations (10.26s)
    --- PASS: TestSanity_CheckForUpdate_Combinations/matching (1.36s)
    --- PASS: TestSanity_CheckForUpdate_Combinations/non_matching (0.29s)
    --- PASS: TestSanity_CheckForUpdate_Combinations/after_delete (8.61s)
=== RUN   TestSanity_CheckForUpdateBulk_MixedResults

────────────────────────────────────────────────────────────────────────────────
[14] TestSanity_CheckForUpdateBulk_MixedResults
────────────────────────────────────────────────────────────────────────────────
Handling connection for 9000
    checkforupdate_test.go:82: Check attempt 1: got ALLOWED_FALSE, want ALLOWED_TRUE
    checkforupdate_test.go:82: Check returned ALLOWED_TRUE on attempt 2
--- PASS: TestSanity_CheckForUpdateBulk_MixedResults (1.38s)
=== RUN   TestSanity_ReportDeleteReReport_Revive

────────────────────────────────────────────────────────────────────────────────
[15] TestSanity_ReportDeleteReReport_Revive
────────────────────────────────────────────────────────────────────────────────
Handling connection for 9000
    lifecycle_test.go:26: Check attempt 1: got ALLOWED_FALSE, want ALLOWED_TRUE
    lifecycle_test.go:26: Check returned ALLOWED_TRUE on attempt 2
    lifecycle_test.go:34: Check attempt 1: got ALLOWED_TRUE, want ALLOWED_FALSE
    lifecycle_test.go:34: Check attempt 2: got ALLOWED_TRUE, want ALLOWED_FALSE
    lifecycle_test.go:34: Check attempt 3: got ALLOWED_TRUE, want ALLOWED_FALSE
    lifecycle_test.go:34: Check attempt 4: got ALLOWED_TRUE, want ALLOWED_FALSE
    lifecycle_test.go:34: Check attempt 5: got ALLOWED_TRUE, want ALLOWED_FALSE
    lifecycle_test.go:34: Check returned ALLOWED_FALSE on attempt 6
    lifecycle_test.go:47: Check attempt 1: got ALLOWED_FALSE, want ALLOWED_TRUE
    lifecycle_test.go:47: Check returned ALLOWED_TRUE on attempt 2
--- PASS: TestSanity_ReportDeleteReReport_Revive (7.99s)
=== RUN   TestSanity_MultiResourceChurn

────────────────────────────────────────────────────────────────────────────────
[16] TestSanity_MultiResourceChurn
────────────────────────────────────────────────────────────────────────────────
Handling connection for 9000
    lifecycle_test.go:71: Check attempt 1: got ALLOWED_FALSE, want ALLOWED_TRUE
    lifecycle_test.go:71: Check returned ALLOWED_TRUE on attempt 2
    lifecycle_test.go:72: Check returned ALLOWED_TRUE on attempt 1
    lifecycle_test.go:73: Check returned ALLOWED_TRUE on attempt 1
    lifecycle_test.go:82: Check attempt 1: got ALLOWED_TRUE, want ALLOWED_FALSE
    lifecycle_test.go:82: Check attempt 2: got ALLOWED_TRUE, want ALLOWED_FALSE
    lifecycle_test.go:82: Check attempt 3: got ALLOWED_TRUE, want ALLOWED_FALSE
    lifecycle_test.go:82: Check attempt 4: got ALLOWED_TRUE, want ALLOWED_FALSE
    lifecycle_test.go:82: Check attempt 5: got ALLOWED_TRUE, want ALLOWED_FALSE
    lifecycle_test.go:82: Check attempt 6: got ALLOWED_TRUE, want ALLOWED_FALSE
    lifecycle_test.go:82: Check attempt 7: got ALLOWED_TRUE, want ALLOWED_FALSE
    lifecycle_test.go:82: Check attempt 8: got ALLOWED_TRUE, want ALLOWED_FALSE
    lifecycle_test.go:82: Check returned ALLOWED_FALSE on attempt 9
    lifecycle_test.go:93: Check attempt 1: got ALLOWED_TRUE, want ALLOWED_FALSE
    lifecycle_test.go:93: Check attempt 2: got ALLOWED_TRUE, want ALLOWED_FALSE
    lifecycle_test.go:93: Check attempt 3: got ALLOWED_TRUE, want ALLOWED_FALSE
    lifecycle_test.go:93: Check attempt 4: got ALLOWED_TRUE, want ALLOWED_FALSE
    lifecycle_test.go:93: Check attempt 5: got ALLOWED_TRUE, want ALLOWED_FALSE
    lifecycle_test.go:93: Check attempt 6: got ALLOWED_TRUE, want ALLOWED_FALSE
    lifecycle_test.go:93: Check attempt 7: got ALLOWED_TRUE, want ALLOWED_FALSE
    lifecycle_test.go:93: Check attempt 8: got ALLOWED_TRUE, want ALLOWED_FALSE
    lifecycle_test.go:93: Check attempt 9: got ALLOWED_TRUE, want ALLOWED_FALSE
    lifecycle_test.go:93: Check attempt 10: got ALLOWED_TRUE, want ALLOWED_FALSE
    lifecycle_test.go:93: Check attempt 11: got ALLOWED_TRUE, want ALLOWED_FALSE
    lifecycle_test.go:93: Check returned ALLOWED_FALSE on attempt 12
    lifecycle_test.go:94: Check attempt 1: got ALLOWED_TRUE, want ALLOWED_FALSE
    lifecycle_test.go:94: Check attempt 2: got ALLOWED_TRUE, want ALLOWED_FALSE
    lifecycle_test.go:94: Check returned ALLOWED_FALSE on attempt 3
--- PASS: TestSanity_MultiResourceChurn (23.53s)
PASS

════════════════════════════════════════════════════════════════════════════════
  SANITY TEST REPORT
════════════════════════════════════════════════════════════════════════════════

▸ [1] TestSanity_CheckBulk_MultipleHosts  [PASS]
  1. ReportResource host/bulk-host-a-1777651696687884000 (hbi, workspace=ws-bulk-a-1777651696687906000)
     DB: ver=0 gen=0 tombstone=false
  2. ReportResource host/bulk-host-b-1777651696687898000 (hbi, workspace=ws-bulk-b-1777651696687914000)
     DB: ver=0 gen=0 tombstone=false
  3. Check host/bulk-host-a-1777651696687884000 (workspace=ws-bulk-a-1777651696687906000)
     → ALLOWED_TRUE (attempt 2)
  4. Check host/bulk-host-b-1777651696687898000 (workspace=ws-bulk-b-1777651696687914000)
     → ALLOWED_TRUE (attempt 1)
  5. CheckBulk (3 items)
     → host/bulk-host-a-1777651696687884000+wsA=ALLOWED_TRUE, host/bulk-host-b-1777651696687898000+wsB=ALLOWED_TRUE, host/bulk-host-a-1777651696687884000+wsWrong=ALLOWED_FALSE
  6. DeleteResource host/bulk-host-b-1777651696687898000 (hbi)
     DB: ver=1 gen=0 tombstone=true
  7. DeleteResource host/bulk-host-a-1777651696687884000 (hbi)
     DB: ver=1 gen=0 tombstone=true

▸ [2] TestSanity_CheckSelf_ReturnsError  [PASS]
  1. ReportResource host/self-host-1777651705556291000 (hbi, workspace=ws-self-1777651705556324000)
     DB: ver=0 gen=0 tombstone=false
  2. CheckSelf (expected error)
     → error: rpc error: code = PermissionDenied desc = meta authorization denied
  3. DeleteResource host/self-host-1777651705556291000 (hbi)
     DB: ver=1 gen=0 tombstone=true

▸ [3] TestSanity_CheckSelfBulk_ReturnsError  [PASS]
  1. ReportResource host/selfbulk-host-1777651705868756000 (hbi, workspace=ws-selfbulk-1777651705868808000)
     DB: ver=0 gen=0 tombstone=false
  2. CheckSelfBulk (expected error)
     → error: rpc error: code = PermissionDenied desc = meta authorization denied
  3. DeleteResource host/selfbulk-host-1777651705868756000 (hbi)
     DB: ver=1 gen=0 tombstone=true

▸ [4] TestSanity_ReportHost_CheckAllowed  [PASS]
  1. ReportResource host/sanity-host-1777651706202708000 (hbi, workspace=ws-1777651706202759000)
     DB: ver=0 gen=0 tombstone=false
  2. Check host/sanity-host-1777651706202708000 (workspace=ws-1777651706202759000)
     → ALLOWED_TRUE (attempt 2)
  3. DeleteResource host/sanity-host-1777651706202708000 (hbi)
     DB: ver=1 gen=0 tombstone=true

▸ [5] TestSanity_Check_WrongWorkspace  [PASS]
  1. ReportResource host/sanity-host-wrong-1777651707786212000 (hbi, workspace=ws-right-1777651707786241000)
     DB: ver=0 gen=0 tombstone=false
  2. Check host/sanity-host-wrong-1777651707786212000 (workspace=ws-wrong-1777651707786266000)
     → ALLOWED_FALSE (attempt 1)
  3. DeleteResource host/sanity-host-wrong-1777651707786212000 (hbi)
     DB: ver=1 gen=0 tombstone=true

▸ [6] TestSanity_DeleteHost_AccessLost  [PASS]
  1. ReportResource host/sanity-del-host-1777651708128547000 (hbi, workspace=ws-1777651708128585000)
     DB: ver=0 gen=0 tombstone=false
  2. Check host/sanity-del-host-1777651708128547000 (workspace=ws-1777651708128585000)
     → ALLOWED_TRUE (attempt 2)
  3. DeleteResource host/sanity-del-host-1777651708128547000 (hbi)
     DB: ver=1 gen=0 tombstone=true
  4. Check host/sanity-del-host-1777651708128547000 (workspace=ws-1777651708128585000)
     → ALLOWED_FALSE (attempt 3)

▸ [7] TestSanity_Check_Combinations/matching_workspace  [PASS]
  1. ReportResource host/combo-matching_workspace-1777651711620579000 (hbi, workspace=ws-a-1777651711620630000)
     DB: ver=0 gen=0 tombstone=false
  2. Check host/combo-matching_workspace-1777651711620579000 (workspace=ws-a-1777651711620630000)
     → ALLOWED_TRUE (attempt 1)
  3. DeleteResource host/combo-matching_workspace-1777651711620579000 (hbi)
     DB: ver=1 gen=0 tombstone=true

▸ [8] TestSanity_Check_Combinations/non_matching_workspace  [PASS]
  1. ReportResource host/combo-non_matching_workspace-1777651712018644000 (hbi, workspace=ws-a-1777651712018646000)
     DB: ver=0 gen=0 tombstone=false
  2. Check host/combo-non_matching_workspace-1777651712018644000 (workspace=ws-b-1777651712018700000)
     → ALLOWED_FALSE (attempt 1)
  3. DeleteResource host/combo-non_matching_workspace-1777651712018644000 (hbi)
     DB: ver=1 gen=0 tombstone=true

▸ [9] TestSanity_Check_Combinations/no_instance_in_check_ref  [PASS]
  1. ReportResource host/combo-no_instance_in_check_ref-1777651712388678000 (hbi, workspace=ws-f-1777651712388681000)
     DB: ver=0 gen=0 tombstone=false
  2. Check host/combo-no_instance_in_check_ref-1777651712388678000 (workspace=ws-f-1777651712388681000)
     → ALLOWED_TRUE (attempt 2)
  3. DeleteResource host/combo-no_instance_in_check_ref-1777651712388678000 (hbi)
     DB: ver=1 gen=0 tombstone=true

▸ [10] TestSanity_Check_Combinations/with_instance_in_check_ref  [PASS]
  1. ReportResource host/combo-with_instance_in_check_ref-1777651713810387000 (hbi, workspace=ws-g-1777651713810390000)
     DB: ver=0 gen=0 tombstone=false
  2. Check host/combo-with_instance_in_check_ref-1777651713810387000 (workspace=ws-g-1777651713810390000)
     → ALLOWED_TRUE (attempt 2)
  3. DeleteResource host/combo-with_instance_in_check_ref-1777651713810387000 (hbi)
     DB: ver=1 gen=0 tombstone=true

▸ [11] TestSanity_CheckForUpdate_Combinations/matching  [PASS]
  1. ReportResource host/cfu-matching-1777651715234790000 (hbi, workspace=ws-cfu-a-1777651715234859000)
     DB: ver=0 gen=0 tombstone=false
  2. CheckForUpdate host/cfu-matching-1777651715234790000 (workspace=ws-cfu-a-1777651715234859000)
     → ALLOWED_TRUE (attempt 2) +token
  3. DeleteResource host/cfu-matching-1777651715234790000 (hbi)
     DB: ver=1 gen=0 tombstone=true

▸ [12] TestSanity_CheckForUpdate_Combinations/non_matching  [PASS]
  1. ReportResource host/cfu-non_matching-1777651716594184000 (hbi, workspace=ws-cfu-a-1777651716594185000)
     DB: ver=0 gen=0 tombstone=false
  2. CheckForUpdate host/cfu-non_matching-1777651716594184000 (workspace=ws-cfu-b-1777651716594240000)
     → ALLOWED_FALSE (attempt 1) +token
  3. DeleteResource host/cfu-non_matching-1777651716594184000 (hbi)
     DB: ver=1 gen=0 tombstone=true

▸ [13] TestSanity_CheckForUpdate_Combinations/after_delete  [PASS]
  1. ReportResource host/cfu-after_delete-1777651716889255000 (hbi, workspace=ws-cfu-d-1777651716889257000)
     DB: ver=0 gen=0 tombstone=false
  2. Check host/cfu-after_delete-1777651716889255000 (workspace=ws-cfu-d-1777651716889257000)
     → ALLOWED_TRUE (attempt 2)
  3. DeleteResource host/cfu-after_delete-1777651716889255000 (hbi)
     DB: ver=1 gen=0 tombstone=true
  4. Check host/cfu-after_delete-1777651716889255000 (workspace=ws-cfu-d-1777651716889257000)
     → ALLOWED_FALSE (attempt 8)
  5. CheckForUpdate host/cfu-after_delete-1777651716889255000 (workspace=ws-cfu-d-1777651716889257000)
     → ALLOWED_FALSE (attempt 1) +token

▸ [14] TestSanity_CheckForUpdateBulk_MixedResults  [PASS]
  1. ReportResource host/cfubulk-1777651725497323000 (hbi, workspace=ws-cfubulk-a-1777651725497338000)
     DB: ver=0 gen=0 tombstone=false
  2. Check host/cfubulk-1777651725497323000 (workspace=ws-cfubulk-a-1777651725497338000)
     → ALLOWED_TRUE (attempt 2)
  3. CheckForUpdateBulk (2 items)
     → host/cfubulk-1777651725497323000+wsA=ALLOWED_TRUE, host/cfubulk-1777651725497323000+wsB=ALLOWED_FALSE
  4. DeleteResource host/cfubulk-1777651725497323000 (hbi)
     DB: ver=1 gen=0 tombstone=true

▸ [15] TestSanity_ReportDeleteReReport_Revive  [PASS]
  1. ReportResource host/revive-host-1777651726875901000 (hbi, workspace=ws-revive-1777651726875964000)
     DB: ver=0 gen=0 tombstone=false
  2. Check host/revive-host-1777651726875901000 (workspace=ws-revive-1777651726875964000)
     → ALLOWED_TRUE (attempt 2)
  3. DeleteResource host/revive-host-1777651726875901000 (hbi)
     DB: ver=1 gen=0 tombstone=true
  4. Check host/revive-host-1777651726875901000 (workspace=ws-revive-1777651726875964000)
     → ALLOWED_FALSE (attempt 6)
  5. ReportResource host/revive-host-1777651726875901000 (hbi, workspace=ws-revive-1777651726875964000)
     DB: ver=0 gen=1 tombstone=false
  6. Check host/revive-host-1777651726875901000 (workspace=ws-revive-1777651726875964000)
     → ALLOWED_TRUE (attempt 2)
  7. DeleteResource host/revive-host-1777651726875901000 (hbi)
     DB: ver=1 gen=1 tombstone=true

▸ [16] TestSanity_MultiResourceChurn  [PASS]
  1. ReportResource host/churn-a-1777651734865978000 (hbi, workspace=ws-churn-1777651734866148000)
     DB: ver=0 gen=0 tombstone=false
  2. ReportResource host/churn-b-1777651734866040000 (hbi, workspace=ws-churn-1777651734866148000)
     DB: ver=0 gen=0 tombstone=false
  3. ReportResource host/churn-c-1777651734866091000 (hbi, workspace=ws-churn-1777651734866148000)
     DB: ver=0 gen=0 tombstone=false
  4. Check host/churn-a-1777651734865978000 (workspace=ws-churn-1777651734866148000)
     → ALLOWED_TRUE (attempt 2)
  5. Check host/churn-b-1777651734866040000 (workspace=ws-churn-1777651734866148000)
     → ALLOWED_TRUE (attempt 1)
  6. Check host/churn-c-1777651734866091000 (workspace=ws-churn-1777651734866148000)
     → ALLOWED_TRUE (attempt 1)
  7. DeleteResource host/churn-b-1777651734866040000 (hbi)
     DB: ver=1 gen=0 tombstone=true
  8. Check host/churn-b-1777651734866040000 (workspace=ws-churn-1777651734866148000)
     → ALLOWED_FALSE (attempt 9)
  9. DeleteResource host/churn-a-1777651734865978000 (hbi)
     DB: ver=1 gen=0 tombstone=true
  10. DeleteResource host/churn-c-1777651734866091000 (hbi)
     DB: ver=1 gen=0 tombstone=true
  11. Check host/churn-a-1777651734865978000 (workspace=ws-churn-1777651734866148000)
     → ALLOWED_FALSE (attempt 12)
  12. Check host/churn-c-1777651734866091000 (workspace=ws-churn-1777651734866148000)
     → ALLOWED_FALSE (attempt 3)

────────────────────────────────────────────────────────────────────────────────
  Total: 16  |  Passed: 16  |  Failed: 0
────────────────────────────────────────────────────────────────────────────────

ok  	github.com/project-kessel/inventory-api/test/e2e/sanity	63.194s
==============================================
[INFO] All sanity tests PASSED
[INFO] Cleaning up...
[INFO] Cleanup complete

@Rajagopalan-Ranganathan
Copy link
Copy Markdown
Contributor

/lgtm

Copy link
Copy Markdown
Contributor

@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.

🧹 Nitpick comments (1)
internal/service/tuples/tuples_test.go (1)

60-63: ⚡ Quick win

Add error-path coverage for the new lock/fencing validation.

These updates only verify successful conversion, but toAcquireLockCommand and fencingCheckFromProto now fail on empty or whitespace-only lock ids/tokens. A couple of negative cases here would lock in the main behavior change from this PR.

Example test shape
 func TestToAcquireLockCommand(t *testing.T) {
-	req := &pb.AcquireLockRequest{
-		LockId: "lock-123",
-	}
-
-	cmd, err := toAcquireLockCommand(req)
-
-	require.NoError(t, err)
-	expectedLockId := model.DeserializeLockId("lock-123")
-	assert.Equal(t, expectedLockId, cmd.LockId)
+	t.Run("valid lock id", func(t *testing.T) {
+		req := &pb.AcquireLockRequest{LockId: "lock-123"}
+		cmd, err := toAcquireLockCommand(req)
+		require.NoError(t, err)
+		assert.Equal(t, model.DeserializeLockId("lock-123"), cmd.LockId)
+	})
+
+	t.Run("empty lock id", func(t *testing.T) {
+		_, err := toAcquireLockCommand(&pb.AcquireLockRequest{LockId: "   "})
+		require.Error(t, err)
+		assert.Contains(t, err.Error(), "invalid lock id")
+	})
 }

Also applies to: 132-135, 179-183

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

In `@internal/service/tuples/tuples_test.go` around lines 60 - 63, Add negative
test cases in tuples_test.go that exercise the new validation paths in
toAcquireLockCommand and fencingCheckFromProto by passing empty string and
whitespace-only values for lock IDs and tokens (e.g., call
fencingCheckFromProto/toAcquireLockCommand with "" and "  "). Assert that these
calls return an error (and do not produce valid
model.DeserializeLockId/model.DeserializeLockToken results) instead of
succeeding; update the existing success checks around
expectedLockId/expectedLockToken to remain as positive cases and add
corresponding failure assertions for the empty/whitespace inputs (also add
analogous negative assertions for the other test blocks covering the fencing
conversion at the other locations).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@internal/service/tuples/tuples_test.go`:
- Around line 60-63: Add negative test cases in tuples_test.go that exercise the
new validation paths in toAcquireLockCommand and fencingCheckFromProto by
passing empty string and whitespace-only values for lock IDs and tokens (e.g.,
call fencingCheckFromProto/toAcquireLockCommand with "" and "  "). Assert that
these calls return an error (and do not produce valid
model.DeserializeLockId/model.DeserializeLockToken results) instead of
succeeding; update the existing success checks around
expectedLockId/expectedLockToken to remain as positive cases and add
corresponding failure assertions for the empty/whitespace inputs (also add
analogous negative assertions for the other test blocks covering the fencing
conversion at the other locations).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: fe5f3b12-f5fa-4593-bf3e-ccc46a6ec5b0

📥 Commits

Reviewing files that changed from the base of the PR and between dea2e2a and e17c2a1.

📒 Files selected for processing (5)
  • internal/biz/usecase/tuples/commands.go
  • internal/biz/usecase/tuples/tuple_crud_usecase.go
  • internal/biz/usecase/tuples/tuple_crud_usecase_test.go
  • internal/service/tuples/tuples.go
  • internal/service/tuples/tuples_test.go

@snehagunta snehagunta merged commit 79b7efe into project-kessel:main May 1, 2026
16 of 17 checks 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.

2 participants