Skip to content

feat: bid precheck#363

Open
troian wants to merge 1 commit intomainfrom
precheck
Open

feat: bid precheck#363
troian wants to merge 1 commit intomainfrom
precheck

Conversation

@troian
Copy link
Member

@troian troian commented Feb 27, 2026

No description provided.

@troian troian requested a review from a team as a code owner February 27, 2026 18:23
@coderabbitai
Copy link

coderabbitai bot commented Feb 27, 2026

Walkthrough

Implements bid screening for provider-group evaluation via a new ScreenBid function and ScreenBidParams/Result types; replaces Validate-based APIs with BidScreening/GatewayClient across service and gateway layers; adds dry-run reservation support in cluster inventory; updates mocks, tests, and dependency versions.

Changes

Cohort / File(s) Summary
Bid Screening Core Logic
bidengine/screening.go, bidengine/screening_test.go
Adds ScreenBid, ScreenBidParams and ScreenBidResult. Centralizes eligibility checks (provider/order attributes, resources, volumes, auditor signatures, hostnames), accumulates failure reasons, and includes extensive unit tests and mocks.
Bid Engine Refactoring
bidengine/order.go, bidengine/order_test.go, bidengine/pricing_test.go
Refactors shouldBid to call ScreenBid instead of inline checks; updates tests to use sdkutil.DenomUakt for coin denominations.
Bidengine Service Interface
bidengine/service.go
Adds ProviderAttrService() ProviderAttrSignatureService to expose provider attribute signature service.
Provider Service Layer
service.go
Introduces GatewayClient/BidScreeningClient changes, adds BidScreening implementation (validate, screen, dry-run reserve, price calc), updates StatusV1 to providerv1 types, and adds context owner helpers.
Gateway gRPC Server
gateway/grpc/server.go
Replaces StatusClient with GatewayClient and adds BidScreening gRPC handler delegating to client.
Gateway REST API
gateway/rest/router.go, gateway/rest/router_test.go, gateway/rest/integration_test.go
Replaces Validate flow with BidScreening, supports parsing both BidScreeningRequest and bare GroupSpec, updates mocks and tests to use BidScreeningResponse.
Mock Implementations
mocks/client/Client_mock.go, mocks/client/GatewayClient_mock.go
Removes old Validate mock, adds GatewayClient mock with BidScreening, Status, and StatusV1 methods and helpers; updates Client mock to expose BidScreening.
Cluster Mock Updates
mocks/cluster/Cluster_mock.go, mocks/cluster/Service_mock.go
Extends Reserve mock signatures to accept variadic InventoryOption arguments and forwards them in mock plumbing.
Cluster Inventory & Service
cluster/inventory.go, cluster/service.go
Adds dry-run flag to inventory requests, makes reserve/Reserve accept variadic options, and prevents state mutation/logging/metrics when dry-run is enabled.
Operator GPU Inventory
operator/inventory/node-discovery.go, operator/inventory/node-discovery_test.go
Resets GPU allocatable/capacity when device plugin disappears; clamps pod GPU allocations to Allocatable; adds tests for GPU disappearance and allocation clamping.
Mocks & Config / Dependencies
.mockery.yaml, go.mod
Adds GatewayClient to .mockery.yaml interfaces; bumps Go toolchain and refreshes numerous dependencies and module versions (cometbft, protobuf, cosmos forks, pkg.akt.dev modules, etc.).

Sequence Diagram

sequenceDiagram
    participant Client as Gateway Client
    participant Service as Provider Service
    participant ScreenBid as Bidengine<br/>ScreenBid
    participant Attrs as Provider Attr Service
    participant Cluster as Cluster<br/>Reserve
    participant Pricing as Pricing<br/>Engine

    Client->>Service: BidScreening(req)
    Service->>Service: Validate GroupSpec
    Service->>ScreenBid: ScreenBid(group, params)
    ScreenBid->>Attrs: Fetch provider/auditor attributes
    Attrs-->>ScreenBid: Attributes / Signatures
    ScreenBid->>ScreenBid: Check attrs, resources, volumes, hostnames, auditor sigs
    ScreenBid-->>Service: ScreenBidResult

    alt Screening Passed
        Service->>Cluster: Reserve(group, DryRun=true)
        Cluster-->>Service: Reservation (allocated resources)
        Service->>Pricing: Calculate price(from reservation)
        Pricing-->>Service: Price
        Service-->>Client: BidScreeningResponse(Passed=true, Price)
    else Screening Failed
        Service-->>Client: BidScreeningResponse(Passed=false, Reasons)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hop through specs and check each field,
Screening bids with logic sealed,
Dry-run prance, no state undone,
Mocks and tests all say “well done!”,
A rabbit’s cheer for work well-pealed!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.66% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive No pull request description was provided by the author, making it impossible to assess whether the description relates to the changeset. Add a description explaining the purpose, scope, and rationale behind the bid precheck feature implementation.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: bid precheck' accurately summarizes the main change—implementing a bid pre-screening/validation feature across the provider system.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch precheck

Warning

Tools execution failed with the following error:

Failed to run tools: 13 INTERNAL: Received RST_STREAM with code 2 (Internal server error)


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

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: 4

🧹 Nitpick comments (4)
bidengine/order.go (1)

519-539: Propagate the order context into screening instead of context.Background().

The run function creates a lifecycle context at line 175, but shouldBid doesn't accept or pass it to ScreenBid. Line 527 uses context.Background(), bypassing the order's context for cancellation and timeout behavior. Update shouldBid to accept ctx from its caller and use it in the ScreenBid call.

Proposed refactor
-func (o *order) shouldBid(group *dtypes.Group) (bool, error) {
+func (o *order) shouldBid(ctx context.Context, group *dtypes.Group) (bool, error) {
-			shouldBidCh = runner.Do(func() runner.Result {
-				return runner.NewResult(o.shouldBid(group))
+			shouldBidCh = runner.Do(func() runner.Result {
+				return runner.NewResult(o.shouldBid(ctx, group))
 			})
-	result, err := ScreenBid(context.Background(), group, params)
+	result, err := ScreenBid(ctx, group, params)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bidengine/order.go` around lines 519 - 539, The screening call currently uses
context.Background() which bypasses the order lifecycle context; change the
shouldBid function signature to accept a context.Context (e.g., ctx) and replace
the context.Background() call with that ctx when calling ScreenBid; update the
caller(s) (notably run which creates the lifecycle context) to pass its ctx into
shouldBid, and ensure signatures and any tests/uses of shouldBid and
ScreenBidParams are updated accordingly so cancellation/timeouts propagate
correctly.
gateway/rest/router_test.go (2)

492-498: Mock setup is unnecessary for unauthorized test.

The mock BidScreening expectation at Line 498 will never be invoked because the request is unauthorized (empty authTypes at Line 491) and returns HTTP 401 before reaching the handler logic that calls BidScreening. This doesn't cause test failures, but the mock setup is dead code.

🧹 Suggested cleanup
 func TestRouteValidateUnauthorized(t *testing.T) {
 	runRouterTest(t, []routerTestAuth{}, func(test *routerTest, hdr http.Header) {
-		price := testutil.AkashDecCoin(t, 200)
-		screenResp := &providerv1.BidScreeningResponse{
-			Passed: true,
-			Price:  &price,
-		}
-
-		test.pclient.On("BidScreening", mock.Anything, mock.Anything).Return(screenResp, nil)
-
 		uri, err := apclient.MakeURI(test.host, apclient.ValidatePath())
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gateway/rest/router_test.go` around lines 492 - 498, Remove the unnecessary
mock setup for BidScreening in this unauthorized test: the test sets authTypes
empty so the request returns 401 before router calls the client, therefore the
test.pclient.On("BidScreening", ...) expectation (and variables screenResp/price
if only used for that) is dead code; either delete the test.pclient.On call and
its related screenResp/price setup from this test, or move that setup into the
authorized test that exercises the BidScreening call.

549-549: Mock will not be invoked due to early return on empty body.

Similar to the unauthorized test, this mock setup won't be called because the handler returns HTTP 400 for empty body before invoking BidScreening. The test correctly expects StatusBadRequest, so the mock is unnecessary.

🧹 Suggested cleanup
 func TestRouteValidateFailsEmptyBody(t *testing.T) {
 	runRouterTest(t, []routerTestAuth{routerTestAuthCert, routerTestAuthJWT}, func(test *routerTest, hdr http.Header) {
-		test.pclient.On("BidScreening", mock.Anything, mock.Anything).Return((*providerv1.BidScreeningResponse)(nil), errGeneric)
-
 		uri, err := apclient.MakeURI(test.host, apclient.ValidatePath())
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gateway/rest/router_test.go` at line 549, The mock setup
test.pclient.On("BidScreening", mock.Anything,
mock.Anything).Return((*providerv1.BidScreeningResponse)(nil), errGeneric) is
dead because the handler returns HTTP 400 on an empty request body before
calling BidScreening; remove this mock expectation from the test (or
alternatively, if you intended to exercise BidScreening, provide a non-empty
valid request body) so the test only asserts StatusBadRequest without relying on
a mock invocation.
bidengine/screening_test.go (1)

141-161: Consider a more robust assertion for validation errors.

The check at Lines 153-159 determines if a reason is a validation error by comparing string length against a hardcoded prefix length. This is fragile and could break if the prefix changes. Consider using strings.HasPrefix instead.

♻️ Suggested improvement
+	"strings"
 ...
-	hasValidationError := false
-	for _, reason := range result.Reasons {
-		if len(reason) > len("group validation error: ") {
-			hasValidationError = true
-			break
-		}
-	}
-	require.True(t, hasValidationError, "expected a group validation error reason")
+	hasValidationError := false
+	for _, reason := range result.Reasons {
+		if strings.HasPrefix(reason, "group validation error:") {
+			hasValidationError = true
+			break
+		}
+	}
+	require.True(t, hasValidationError, "expected a group validation error reason")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bidengine/screening_test.go` around lines 141 - 161, The test
TestScreenBid_FailsValidateBasic currently detects a group validation error by
comparing reason string lengths; update it to robustly check each result.Reasons
entry with strings.HasPrefix(reason, "group validation error: ") instead,
importing the "strings" package, and assert that at least one reason matches
that prefix (e.g., set hasValidationError when prefix matches and require.True
afterward) so it no longer depends on hardcoded length checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@bidengine/screening.go`:
- Around line 39-55: Add defensive nil checks at the start of ScreenBid to avoid
panics: validate that the incoming pointers group, params.Provider, and
params.AttrService are non-nil and return a descriptive error if any are nil
before calling group.GroupSpec.MatchAttributes, params.Attributes.SubsetOf, or
params.AttrService.GetAttributes; ensure you reference the same error messages
consistently and bail out early from ScreenBid when any required input is
missing.

In `@operator/inventory/node-discovery_test.go`:
- Around line 17-20: The helper newKubeNode currently accepts an unused varying
parameter name which triggers golangci-lint unparam; change its signature to
remove the name parameter (e.g., newKubeNode(gpuCount int64)) and set a fixed
node name inside the function, then update all test call sites that currently
pass a name to only pass the gpuCount argument (or none if defaulting), ensuring
references to newKubeNode in tests are adjusted accordingly so the helper
compiles and linter warnings disappear.

In `@service.go`:
- Around line 272-273: The owner value obtained via ownerFromCtx(ctx) must be
checked for missing/empty before performing hostname-based screening; update the
code that builds the availability check request (the block using Owner:
ownerFromCtx(ctx) and Hostnames: req.GetHostnames()) to first call
ownerFromCtx(ctx), return an explicit failure response (with a clear reason like
"missing owner in context") if the owner is absent, and only proceed to
construct/submit the hostname screening request when the owner is present so
hostname checks do not produce misleading availability reasons.
- Around line 299-305: The precheck pricing request (priceReq of type
bidengine.Request) is missing Owner and PricePrecision causing inconsistent
pricing; update the construction in service.go (where priceReq is built before
calling s.config.BidPricingStrategy.CalculatePrice) to set Owner using
ownerFromCtx(ctx) and set PricePrecision to DefaultPricePrecision so the request
matches the production path (see Request/priceReq and bidengine.Request usage).

---

Nitpick comments:
In `@bidengine/order.go`:
- Around line 519-539: The screening call currently uses context.Background()
which bypasses the order lifecycle context; change the shouldBid function
signature to accept a context.Context (e.g., ctx) and replace the
context.Background() call with that ctx when calling ScreenBid; update the
caller(s) (notably run which creates the lifecycle context) to pass its ctx into
shouldBid, and ensure signatures and any tests/uses of shouldBid and
ScreenBidParams are updated accordingly so cancellation/timeouts propagate
correctly.

In `@bidengine/screening_test.go`:
- Around line 141-161: The test TestScreenBid_FailsValidateBasic currently
detects a group validation error by comparing reason string lengths; update it
to robustly check each result.Reasons entry with strings.HasPrefix(reason,
"group validation error: ") instead, importing the "strings" package, and assert
that at least one reason matches that prefix (e.g., set hasValidationError when
prefix matches and require.True afterward) so it no longer depends on hardcoded
length checks.

In `@gateway/rest/router_test.go`:
- Around line 492-498: Remove the unnecessary mock setup for BidScreening in
this unauthorized test: the test sets authTypes empty so the request returns 401
before router calls the client, therefore the test.pclient.On("BidScreening",
...) expectation (and variables screenResp/price if only used for that) is dead
code; either delete the test.pclient.On call and its related screenResp/price
setup from this test, or move that setup into the authorized test that exercises
the BidScreening call.
- Line 549: The mock setup test.pclient.On("BidScreening", mock.Anything,
mock.Anything).Return((*providerv1.BidScreeningResponse)(nil), errGeneric) is
dead because the handler returns HTTP 400 on an empty request body before
calling BidScreening; remove this mock expectation from the test (or
alternatively, if you intended to exercise BidScreening, provide a non-empty
valid request body) so the test only asserts StatusBadRequest without relying on
a mock invocation.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c194985 and 8dc795e.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (21)
  • .mockery.yaml
  • bidengine/order.go
  • bidengine/order_test.go
  • bidengine/pricing_test.go
  • bidengine/screening.go
  • bidengine/screening_test.go
  • bidengine/service.go
  • cluster/inventory.go
  • cluster/service.go
  • gateway/grpc/server.go
  • gateway/rest/integration_test.go
  • gateway/rest/router.go
  • gateway/rest/router_test.go
  • go.mod
  • mocks/client/Client_mock.go
  • mocks/client/GatewayClient_mock.go
  • mocks/cluster/Cluster_mock.go
  • mocks/cluster/Service_mock.go
  • operator/inventory/node-discovery.go
  • operator/inventory/node-discovery_test.go
  • service.go

Comment on lines +39 to +55
func ScreenBid(_ context.Context, group *dtypes.Group, params ScreenBidParams) (ScreenBidResult, error) {
var reasons []string

// Check 1: provider attribute matching
if !group.GroupSpec.MatchAttributes(params.Provider.Attributes) {
reasons = append(reasons, "incompatible provider attributes")
}

// Check 2: order attribute matching
if !params.Attributes.SubsetOf(group.GroupSpec.Requirements.Attributes) {
reasons = append(reasons, "incompatible order attributes")
}

// Check 3: resource capability matching
attr, err := params.AttrService.GetAttributes()
if err != nil {
return ScreenBidResult{}, fmt.Errorf("fetching provider attributes: %w", err)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add defensive nil preconditions for required inputs.

This path can panic if group, params.Provider, or params.AttrService is nil. Return an error instead of dereferencing immediately.

Proposed fix
 func ScreenBid(_ context.Context, group *dtypes.Group, params ScreenBidParams) (ScreenBidResult, error) {
+	if group == nil {
+		return ScreenBidResult{}, fmt.Errorf("group is nil")
+	}
+	if params.Provider == nil {
+		return ScreenBidResult{}, fmt.Errorf("provider is nil")
+	}
+	if params.AttrService == nil {
+		return ScreenBidResult{}, fmt.Errorf("attribute service is nil")
+	}
+
 	var reasons []string
📝 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
func ScreenBid(_ context.Context, group *dtypes.Group, params ScreenBidParams) (ScreenBidResult, error) {
var reasons []string
// Check 1: provider attribute matching
if !group.GroupSpec.MatchAttributes(params.Provider.Attributes) {
reasons = append(reasons, "incompatible provider attributes")
}
// Check 2: order attribute matching
if !params.Attributes.SubsetOf(group.GroupSpec.Requirements.Attributes) {
reasons = append(reasons, "incompatible order attributes")
}
// Check 3: resource capability matching
attr, err := params.AttrService.GetAttributes()
if err != nil {
return ScreenBidResult{}, fmt.Errorf("fetching provider attributes: %w", err)
func ScreenBid(_ context.Context, group *dtypes.Group, params ScreenBidParams) (ScreenBidResult, error) {
if group == nil {
return ScreenBidResult{}, fmt.Errorf("group is nil")
}
if params.Provider == nil {
return ScreenBidResult{}, fmt.Errorf("provider is nil")
}
if params.AttrService == nil {
return ScreenBidResult{}, fmt.Errorf("attribute service is nil")
}
var reasons []string
// Check 1: provider attribute matching
if !group.GroupSpec.MatchAttributes(params.Provider.Attributes) {
reasons = append(reasons, "incompatible provider attributes")
}
// Check 2: order attribute matching
if !params.Attributes.SubsetOf(group.GroupSpec.Requirements.Attributes) {
reasons = append(reasons, "incompatible order attributes")
}
// Check 3: resource capability matching
attr, err := params.AttrService.GetAttributes()
if err != nil {
return ScreenBidResult{}, fmt.Errorf("fetching provider attributes: %w", err)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bidengine/screening.go` around lines 39 - 55, Add defensive nil checks at the
start of ScreenBid to avoid panics: validate that the incoming pointers group,
params.Provider, and params.AttrService are non-nil and return a descriptive
error if any are nil before calling group.GroupSpec.MatchAttributes,
params.Attributes.SubsetOf, or params.AttrService.GetAttributes; ensure you
reference the same error messages consistently and bail out early from ScreenBid
when any required input is missing.

Signed-off-by: Artur Troian <[email protected]>
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: 2

♻️ Duplicate comments (1)
bidengine/screening.go (1)

39-55: ⚠️ Potential issue | 🟠 Major

Add defensive nil preconditions for required inputs.

The function can panic if group, params.Provider, or params.AttrService is nil. These are dereferenced at lines 43, 43, and 53 respectively without validation.

🛡️ Proposed fix
 func ScreenBid(_ context.Context, group *dtypes.Group, params ScreenBidParams) (ScreenBidResult, error) {
+	if group == nil {
+		return ScreenBidResult{}, fmt.Errorf("group is nil")
+	}
+	if params.Provider == nil {
+		return ScreenBidResult{}, fmt.Errorf("provider is nil")
+	}
+	if params.AttrService == nil {
+		return ScreenBidResult{}, fmt.Errorf("attribute service is nil")
+	}
+
 	var reasons []string
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bidengine/screening.go` around lines 39 - 55, ScreenBid currently
dereferences group, params.Provider and params.AttrService without checks which
can panic; add defensive nil preconditions at the start of ScreenBid to validate
group != nil, params.Provider != nil and params.AttrService != nil and return a
descriptive error if any are nil (e.g., "nil group", "nil provider in params",
"nil AttrService in params"); update callers/tests if needed and ensure
subsequent calls to group.GroupSpec.MatchAttributes, params.Attributes.SubsetOf
and params.AttrService.GetAttributes only occur after these validations so error
paths are consistent and informative.
🧹 Nitpick comments (2)
gateway/rest/router_test.go (1)

456-487: Add a happy-path test using providerv1.BidScreeningRequest body.

Current coverage exercises the legacy bare GroupSpec payload, but not the new envelope parsing path introduced in the handler.

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

In `@gateway/rest/router_test.go` around lines 456 - 487, Update the
TestRouteValidateOK test to exercise the envelope parsing path by sending a
JSON-encoded providerv1.BidScreeningRequest instead of the bare GroupSpec:
construct a providerv1.BidScreeningRequest whose GroupSpec field contains the
testutil.GroupSpec value, marshal that request to JSON and use it as the HTTP
body for the GET to apclient.ValidatePath(), keep the existing
test.pclient.On("BidScreening", ...) mock return (screenResp) and
headers/content-type handling the same so the handler receives and routes the
enveloped payload through the BidScreening call.
bidengine/screening.go (1)

34-34: Unused Log field in ScreenBidParams.

The Log field is declared but never used within the ScreenBid function. Consider removing it if not needed, or add logging for screening steps.

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

In `@bidengine/screening.go` at line 34, The ScreenBidParams struct contains an
unused Log field; update the code so either remove the Log field from
ScreenBidParams if no per-call logger is required, or wire it into the screening
flow by using that logger inside ScreenBid (e.g., reference ScreenBidParams.Log
within the ScreenBid function to emit screening start/decision/error logs).
Locate the struct definition for ScreenBidParams and the ScreenBid function and
perform the corresponding removal or replacement of any package-level logger
usages with the provided params.Log to ensure the field is actually used or
eliminated.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@bidengine/order.go`:
- Around line 519-525: The ScreenBidParams struct is missing fields used by
ScreenBid's hostname availability check; update the params literal in order.go
to include HostnameService, Owner, and Hostnames so shouldBid doesn't bypass the
precheck. Populate HostnameService from the instance that provides hostname
checks (e.g. o.hostnameService or the relevant service on o.pass), set Owner
from the session or config (e.g. o.session.Owner() or o.cfg.Owner), and set
Hostnames from the config/session (e.g. o.cfg.Hostnames or
o.session.Hostnames()); ensure the added fields match ScreenBidParams' types.

In `@gateway/rest/router.go`:
- Around line 467-480: The fallback accepts malformed envelopes because unknown
top-level fields get ignored and json.Unmarshal into gspec yields a zero-value
GroupSpec; fix by first decoding data into a map[string]json.RawMessage to
detect which top-level keys exist (or alternatively use json.Decoder with
DisallowUnknownFields), then only allow the fallback path when the payload
contains the expected "groupSpec" key (or known BidScreeningRequest keys) —
update the logic around variables screenReq, gspec and data so you return
http.StatusUnprocessableEntity for payloads that contain no recognized top-level
fields instead of silently treating them as an empty GroupSpec.

---

Duplicate comments:
In `@bidengine/screening.go`:
- Around line 39-55: ScreenBid currently dereferences group, params.Provider and
params.AttrService without checks which can panic; add defensive nil
preconditions at the start of ScreenBid to validate group != nil,
params.Provider != nil and params.AttrService != nil and return a descriptive
error if any are nil (e.g., "nil group", "nil provider in params", "nil
AttrService in params"); update callers/tests if needed and ensure subsequent
calls to group.GroupSpec.MatchAttributes, params.Attributes.SubsetOf and
params.AttrService.GetAttributes only occur after these validations so error
paths are consistent and informative.

---

Nitpick comments:
In `@bidengine/screening.go`:
- Line 34: The ScreenBidParams struct contains an unused Log field; update the
code so either remove the Log field from ScreenBidParams if no per-call logger
is required, or wire it into the screening flow by using that logger inside
ScreenBid (e.g., reference ScreenBidParams.Log within the ScreenBid function to
emit screening start/decision/error logs). Locate the struct definition for
ScreenBidParams and the ScreenBid function and perform the corresponding removal
or replacement of any package-level logger usages with the provided params.Log
to ensure the field is actually used or eliminated.

In `@gateway/rest/router_test.go`:
- Around line 456-487: Update the TestRouteValidateOK test to exercise the
envelope parsing path by sending a JSON-encoded providerv1.BidScreeningRequest
instead of the bare GroupSpec: construct a providerv1.BidScreeningRequest whose
GroupSpec field contains the testutil.GroupSpec value, marshal that request to
JSON and use it as the HTTP body for the GET to apclient.ValidatePath(), keep
the existing test.pclient.On("BidScreening", ...) mock return (screenResp) and
headers/content-type handling the same so the handler receives and routes the
enveloped payload through the BidScreening call.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 8dc795e and 062969f.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (21)
  • .mockery.yaml
  • bidengine/order.go
  • bidengine/order_test.go
  • bidengine/pricing_test.go
  • bidengine/screening.go
  • bidengine/screening_test.go
  • bidengine/service.go
  • cluster/inventory.go
  • cluster/service.go
  • gateway/grpc/server.go
  • gateway/rest/integration_test.go
  • gateway/rest/router.go
  • gateway/rest/router_test.go
  • go.mod
  • mocks/client/Client_mock.go
  • mocks/client/GatewayClient_mock.go
  • mocks/cluster/Cluster_mock.go
  • mocks/cluster/Service_mock.go
  • operator/inventory/node-discovery.go
  • operator/inventory/node-discovery_test.go
  • service.go
✅ Files skipped from review due to trivial changes (1)
  • bidengine/screening_test.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • cluster/service.go
  • .mockery.yaml
  • operator/inventory/node-discovery.go
  • bidengine/pricing_test.go
  • bidengine/service.go

Comment on lines +519 to 525
params := ScreenBidParams{
Provider: o.session.Provider(),
Attributes: o.cfg.Attributes,
MaxGroupVolumes: o.cfg.MaxGroupVolumes,
AttrService: o.pass,
Log: o.log,
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Populate full ScreenBidParams to avoid skipping hostname precheck.

shouldBid does not pass HostnameService, Owner, or Hostnames, so ScreenBid’s hostname availability check is effectively disabled on this path.

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

In `@bidengine/order.go` around lines 519 - 525, The ScreenBidParams struct is
missing fields used by ScreenBid's hostname availability check; update the
params literal in order.go to include HostnameService, Owner, and Hostnames so
shouldBid doesn't bypass the precheck. Populate HostnameService from the
instance that provides hostname checks (e.g. o.hostnameService or the relevant
service on o.pass), set Owner from the session or config (e.g. o.session.Owner()
or o.cfg.Owner), and set Hostnames from the config/session (e.g. o.cfg.Hostnames
or o.session.Hostnames()); ensure the added fields match ScreenBidParams' types.

Comment on lines +467 to 480
// Try BidScreeningRequest format first, fall back to bare GroupSpec
var screenReq providerv1.BidScreeningRequest
var gspec dvbeta.GroupSpec

if err := json.Unmarshal(data, &gspec); err != nil {
http.Error(w, err.Error(), http.StatusUnprocessableEntity)
return
if err := json.Unmarshal(data, &screenReq); err == nil && screenReq.GroupSpec != nil && screenReq.GroupSpec.Name != "" {
// parsed as BidScreeningRequest
} else {
// Backward compatible: try bare GroupSpec
if err := json.Unmarshal(data, &gspec); err != nil {
http.Error(w, err.Error(), http.StatusUnprocessableEntity)
return
}
screenReq.GroupSpec = &gspec
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fallback parsing can accept malformed request envelopes as empty specs.

Unknown top-level fields are ignored during fallback unmarshal, so invalid envelope payloads can slip through as zero-value GroupSpec and hit BidScreening instead of returning 422.

Proposed hardening
-		if err := json.Unmarshal(data, &screenReq); err == nil && screenReq.GroupSpec != nil && screenReq.GroupSpec.Name != "" {
+		if err := json.Unmarshal(data, &screenReq); err == nil && screenReq.GroupSpec != nil {
 			// parsed as BidScreeningRequest
 		} else {
 			// Backward compatible: try bare GroupSpec
-			if err := json.Unmarshal(data, &gspec); err != nil {
+			if err := json.Unmarshal(data, &gspec); err != nil || gspec.Name == "" {
 				http.Error(w, err.Error(), http.StatusUnprocessableEntity)
 				return
 			}
 			screenReq.GroupSpec = &gspec
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gateway/rest/router.go` around lines 467 - 480, The fallback accepts
malformed envelopes because unknown top-level fields get ignored and
json.Unmarshal into gspec yields a zero-value GroupSpec; fix by first decoding
data into a map[string]json.RawMessage to detect which top-level keys exist (or
alternatively use json.Decoder with DisallowUnknownFields), then only allow the
fallback path when the payload contains the expected "groupSpec" key (or known
BidScreeningRequest keys) — update the logic around variables screenReq, gspec
and data so you return http.StatusUnprocessableEntity for payloads that contain
no recognized top-level fields instead of silently treating them as an empty
GroupSpec.

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.

1 participant