Skip to content

fix(upstream): update config last scraped time id for agent#1877

Merged
moshloop merged 2 commits intomainfrom
agent-upstream-config-fix
Apr 10, 2026
Merged

fix(upstream): update config last scraped time id for agent#1877
moshloop merged 2 commits intomainfrom
agent-upstream-config-fix

Conversation

@yashmehrotra
Copy link
Copy Markdown
Member

@yashmehrotra yashmehrotra commented Apr 10, 2026

Summary by CodeRabbit

  • Bug Fixes

    • Normalize configuration timestamps for agent setups to ensure accurate tracking of scraping activity across config items.
  • Tests

    • Added test coverage for agent configuration handling and updated test fixtures to use relative timestamps for reliability.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 10, 2026

Benchstat (Other)

Base: 7262acc74e43ab6bda74cd4f9fdb3f23f6888fd9
Head: b92edf83094695ee6c65cdbd0f3aee4a54e4555e

✅ No significant performance changes detected

Full benchstat output
goos: linux
goarch: amd64
pkg: github.com/flanksource/duty/bench
cpu: AMD EPYC 7763 64-Core Processor                
                                                       │ bench-base.txt │           bench-head.txt           │
                                                       │     sec/op     │    sec/op     vs base              │
InsertionForRowsWithAliases/external_users.aliases-4       566.3µ ±  3%   569.6µ ± 14%       ~ (p=0.589 n=6)
InsertionForRowsWithAliases/config_items.external_id-4     1.069m ± 12%   1.067m ± 11%       ~ (p=0.937 n=6)
ResourceSelectorConfigs/name-4                             195.5µ ±  1%   196.3µ ±  1%       ~ (p=0.589 n=6)
ResourceSelectorConfigs/name_and_type-4                    213.6µ ±  3%   215.8µ ±  2%       ~ (p=0.310 n=6)
ResourceSelectorConfigs/tags-4                             28.93m ±  3%   28.58m ±  5%       ~ (p=0.937 n=6)
ResourceSelectorQueryBuild/name-4                          43.14µ ±  1%   43.28µ ±  1%       ~ (p=0.180 n=6)
ResourceSelectorQueryBuild/name_and_type-4                 63.41µ ±  1%   63.63µ ±  1%       ~ (p=0.310 n=6)
ResourceSelectorQueryBuild/tags-4                          17.22µ ±  2%   17.13µ ±  1%       ~ (p=0.180 n=6)
geomean                                                    276.8µ         277.1µ        +0.10%

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 10, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1628a564-bf9d-432f-91a5-0d28f2f3ddea

📥 Commits

Reviewing files that changed from the base of the PR and between f7b44b9 and b92edf8.

📒 Files selected for processing (1)
  • tests/fixtures/dummy/config.go

Walkthrough

The PushData.AddAgentConfig method now normalizes ConfigItemsLastScrapedTime entries by setting ConfigID to the agent's ID when it's uuid.Nil. A new test exercises the method's filtering and remapping behavior; a fixture's timestamps were adjusted to use relative times.

Changes

Cohort / File(s) Summary
AddAgentConfig Normalization
upstream/upstream.go
Added a loop to normalize ConfigItemsLastScrapedTime by assigning the provided agent's ID where ConfigID == uuid.Nil. Existing config item/filtering/remapping logic unchanged.
AddAgentConfig Test Coverage
upstream/upstream_test.go
New test TestAddAgentConfig verifying: nil-ID config item filtering, agent config ID/name assignment, ConfigChanges remapping of nil IDs, system scraper filtering, and ConfigItemsLastScrapedTime normalization.
Fixture Timestamp Adjustment
tests/fixtures/dummy/config.go
Updated NginxIngressPodDeleted fixture timestamps to use relative times based on CurrentTime (now CurrentTime - 2h and CurrentTime - 1h) instead of hard-coded UTC literals.
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: normalizing ConfigItemsLastScrapedTime entries by setting ConfigID to the agent's ID when it is uuid.Nil.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent-upstream-config-fix
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch agent-upstream-config-fix

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

❤️ Share

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

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 10, 2026

Benchstat (RLS)

Base: 7262acc74e43ab6bda74cd4f9fdb3f23f6888fd9
Head: b92edf83094695ee6c65cdbd0f3aee4a54e4555e

📊 5 minor regression(s) (all within 5% threshold)

Benchmark Base Head Change p-value
RLS/Sample-15000/config_types/With_RLS-4 111.1m 114.1m +2.68% 0.002
RLS/Sample-15000/configs/With_RLS-4 111.3m 113.2m +1.63% 0.002
RLS/Sample-15000/config_classes/With_RLS-4 110.5m 111.3m +0.71% 0.002
RLS/Sample-15000/config_changes/With_RLS-4 114.9m 115.5m +0.49% 0.009
RLS/Sample-15000/catalog_changes/With_RLS-4 115.1m 115.5m +0.32% 0.009
✅ 2 improvement(s)
Benchmark Base Head Change p-value
RLS/Sample-15000/config_names/Without_RLS-4 10.35m 10.23m -1.17% 0.002
RLS/Sample-15000/config_changes/Without_RLS-4 4.492m 4.483m -0.21% 0.026
Full benchstat output
goos: linux
goarch: amd64
pkg: github.com/flanksource/duty/bench
cpu: Intel(R) Xeon(R) Platinum 8370C CPU @ 2.80GHz
                                               │ bench-base.txt │          bench-head.txt           │
                                               │     sec/op     │   sec/op     vs base              │
RLS/Sample-15000/catalog_changes/Without_RLS-4      4.498m ± 2%   4.482m ± 4%       ~ (p=0.485 n=6)
RLS/Sample-15000/catalog_changes/With_RLS-4         115.1m ± 0%   115.5m ± 0%  +0.32% (p=0.009 n=6)
RLS/Sample-15000/config_changes/Without_RLS-4       4.492m ± 0%   4.483m ± 0%  -0.21% (p=0.026 n=6)
RLS/Sample-15000/config_changes/With_RLS-4          114.9m ± 0%   115.5m ± 0%  +0.49% (p=0.009 n=6)
RLS/Sample-15000/config_detail/Without_RLS-4        3.357m ± 0%   3.357m ± 1%       ~ (p=0.937 n=6)
RLS/Sample-15000/config_detail/With_RLS-4           111.3m ± 0%   111.6m ± 0%       ~ (p=0.093 n=6)
RLS/Sample-15000/config_names/Without_RLS-4         10.35m ± 1%   10.23m ± 1%  -1.17% (p=0.002 n=6)
RLS/Sample-15000/config_names/With_RLS-4            111.5m ± 1%   111.7m ± 0%       ~ (p=0.065 n=6)
RLS/Sample-15000/config_summary/Without_RLS-4       50.74m ± 1%   50.28m ± 1%       ~ (p=0.180 n=6)
RLS/Sample-15000/config_summary/With_RLS-4          664.0m ± 1%   666.0m ± 0%       ~ (p=0.589 n=6)
RLS/Sample-15000/configs/Without_RLS-4              5.825m ± 1%   5.822m ± 1%       ~ (p=0.937 n=6)
RLS/Sample-15000/configs/With_RLS-4                 111.3m ± 1%   113.2m ± 0%  +1.63% (p=0.002 n=6)
RLS/Sample-15000/analysis_types/Without_RLS-4       3.407m ± 0%   3.398m ± 1%       ~ (p=0.132 n=6)
RLS/Sample-15000/analysis_types/With_RLS-4          3.412m ± 4%   3.407m ± 4%       ~ (p=0.699 n=6)
RLS/Sample-15000/analyzer_types/Without_RLS-4       3.272m ± 1%   3.261m ± 0%       ~ (p=0.310 n=6)
RLS/Sample-15000/analyzer_types/With_RLS-4          3.278m ± 1%   3.300m ± 4%       ~ (p=0.132 n=6)
RLS/Sample-15000/change_types/Without_RLS-4         4.503m ± 1%   4.496m ± 1%       ~ (p=0.937 n=6)
RLS/Sample-15000/change_types/With_RLS-4            4.514m ± 2%   4.505m ± 1%       ~ (p=0.132 n=6)
RLS/Sample-15000/config_classes/Without_RLS-4       2.868m ± 1%   2.867m ± 0%       ~ (p=0.937 n=6)
RLS/Sample-15000/config_classes/With_RLS-4          110.5m ± 0%   111.3m ± 1%  +0.71% (p=0.002 n=6)
RLS/Sample-15000/config_types/Without_RLS-4         3.359m ± 1%   3.351m ± 1%       ~ (p=0.394 n=6)
RLS/Sample-15000/config_types/With_RLS-4            111.1m ± 0%   114.1m ± 0%  +2.68% (p=0.002 n=6)
geomean                                             16.66m        16.69m       +0.14%

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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
upstream/upstream_test.go (1)

1-2: Drop the ABOUTME banner comments.

These top-level comments are boilerplate and don’t add actionable context in this test file.

As per coding guidelines, **/*.go: Only add comments if really necessary; do not add comments that simply explain the code.

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

In `@upstream/upstream_test.go` around lines 1 - 2, Remove the top-of-file
boilerplate banner comments labeled "ABOUTME" from upstream_test.go; open the
test file (upstream_test.go), delete the two ABOUTME comment lines at the top so
the file starts with package declaration or actual test code, and ensure no
other non-actionable comments remain—no code changes required to functions like
Test*; just remove the banner comments to comply with the comment guidelines.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@upstream/upstream_test.go`:
- Around line 15-86: TestAddAgentConfig uses native t.Errorf/t.Fatalf
assertions; replace them with Gomega by creating g := gomega.NewWithT(t) at
start of the test and convert each assertion to g.Expect(...) style—e.g., check
lengths with g.Expect(len(pushData.ConfigItems)).To(Equal(1)), equality checks
for IDs/names with g.Expect(...).To(Equal(...)), and ensure boolean conditions
use appropriate matchers. Also add the github.com/onsi/gomega import and remove
direct t.Fatalf/t.Errorf calls, keeping the rest of the test logic (references:
TestAddAgentConfig, pushData.AddAgentConfig, pushData.ConfigItems,
pushData.ConfigChanges, pushData.ConfigScrapers,
pushData.ConfigItemsLastScrapedTime).

In `@upstream/upstream.go`:
- Around line 357-362: Only remap nil ConfigID entries in
t.ConfigItemsLastScrapedTime when there is actually an agent config item in
t.ConfigItems; before the loop over t.ConfigItemsLastScrapedTime, scan
t.ConfigItems for a config item matching the current agent (e.g. check for
ci.AgentID == agent.ID or whichever field links config items to the agent) and
set a boolean like hasAgentConfig; then perform the existing remap
(t.ConfigItemsLastScrapedTime[i].ConfigID = agent.ID) only if hasAgentConfig is
true, leaving entries untouched otherwise to avoid creating dangling config_id
references.

---

Nitpick comments:
In `@upstream/upstream_test.go`:
- Around line 1-2: Remove the top-of-file boilerplate banner comments labeled
"ABOUTME" from upstream_test.go; open the test file (upstream_test.go), delete
the two ABOUTME comment lines at the top so the file starts with package
declaration or actual test code, and ensure no other non-actionable comments
remain—no code changes required to functions like Test*; just remove the banner
comments to comply with the comment guidelines.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1e7b93e9-c70e-46cb-8c76-dbe6495f3c41

📥 Commits

Reviewing files that changed from the base of the PR and between 7262acc and f7b44b9.

📒 Files selected for processing (2)
  • upstream/upstream.go
  • upstream/upstream_test.go

Comment on lines +15 to +86
func TestAddAgentConfig(t *testing.T) {
agentID := uuid.New()
agent := models.Agent{
ID: agentID,
Name: "test-agent",
}

now := time.Now()

pushData := &PushData{
ConfigItems: []models.ConfigItem{
{
ID: uuid.Nil,
Type: lo.ToPtr("MissionControl::ShouldBeFiltered"),
},
{
ID: uuid.New(),
Type: lo.ToPtr("MissionControl::Agent"),
},
},
ConfigChanges: []models.ConfigChange{
{ConfigID: uuid.Nil.String()},
},
ConfigScrapers: []models.ConfigScraper{
{ID: uuid.Nil},
{ID: uuid.New()},
},
ConfigItemsLastScrapedTime: []models.ConfigItemLastScrapedTime{
{ConfigID: uuid.Nil, LastScrapedTime: &now},
},
}

pushData.AddAgentConfig(agent)

// Config item with uuid.Nil ID should be filtered out
if len(pushData.ConfigItems) != 1 {
t.Fatalf("expected 1 config item, got %d", len(pushData.ConfigItems))
}

// MissionControl::Agent item should have ID set to agent ID
ci := pushData.ConfigItems[0]
if ci.ID != agentID {
t.Errorf("expected agent config item ID to be %s, got %s", agentID, ci.ID)
}
if lo.FromPtr(ci.Name) != "test-agent" {
t.Errorf("expected agent config item name to be 'test-agent', got %s", lo.FromPtr(ci.Name))
}
if lo.FromPtr(ci.ScraperID) != uuid.Nil.String() {
t.Errorf("expected scraper_id to be nil UUID, got %s", lo.FromPtr(ci.ScraperID))
}

// Config changes with uuid.Nil config_id should be remapped
if pushData.ConfigChanges[0].ConfigID != agentID.String() {
t.Errorf("expected config change config_id to be %s, got %s", agentID, pushData.ConfigChanges[0].ConfigID)
}

// System scraper should be filtered out
if len(pushData.ConfigScrapers) != 1 {
t.Fatalf("expected 1 config scraper, got %d", len(pushData.ConfigScrapers))
}
if pushData.ConfigScrapers[0].ID == uuid.Nil {
t.Error("system scraper should have been filtered out")
}

// Last scraped time with uuid.Nil config_id should be remapped to agent ID
if len(pushData.ConfigItemsLastScrapedTime) != 1 {
t.Fatalf("expected 1 last scraped time entry, got %d", len(pushData.ConfigItemsLastScrapedTime))
}
if pushData.ConfigItemsLastScrapedTime[0].ConfigID != agentID {
t.Errorf("expected last scraped time config_id to be %s, got %s", agentID, pushData.ConfigItemsLastScrapedTime[0].ConfigID)
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Use Gomega assertions in this test.

Please migrate these assertions to gomega.NewWithT(t) per test conventions.

Suggested fix
 import (
 	"testing"
 	"time"

 	"github.com/google/uuid"
+	"github.com/onsi/gomega"
 	"github.com/samber/lo"

 	"github.com/flanksource/duty/models"
 )

 func TestAddAgentConfig(t *testing.T) {
+	g := gomega.NewWithT(t)
 	agentID := uuid.New()
 	agent := models.Agent{
 		ID:   agentID,
 		Name: "test-agent",
 	}
@@
 	pushData.AddAgentConfig(agent)

-	if len(pushData.ConfigItems) != 1 {
-		t.Fatalf("expected 1 config item, got %d", len(pushData.ConfigItems))
-	}
+	g.Expect(pushData.ConfigItems).To(gomega.HaveLen(1))

 	ci := pushData.ConfigItems[0]
-	if ci.ID != agentID {
-		t.Errorf("expected agent config item ID to be %s, got %s", agentID, ci.ID)
-	}
-	if lo.FromPtr(ci.Name) != "test-agent" {
-		t.Errorf("expected agent config item name to be 'test-agent', got %s", lo.FromPtr(ci.Name))
-	}
-	if lo.FromPtr(ci.ScraperID) != uuid.Nil.String() {
-		t.Errorf("expected scraper_id to be nil UUID, got %s", lo.FromPtr(ci.ScraperID))
-	}
+	g.Expect(ci.ID).To(gomega.Equal(agentID))
+	g.Expect(lo.FromPtr(ci.Name)).To(gomega.Equal("test-agent"))
+	g.Expect(lo.FromPtr(ci.ScraperID)).To(gomega.Equal(uuid.Nil.String()))

-	if pushData.ConfigChanges[0].ConfigID != agentID.String() {
-		t.Errorf("expected config change config_id to be %s, got %s", agentID, pushData.ConfigChanges[0].ConfigID)
-	}
+	g.Expect(pushData.ConfigChanges).To(gomega.HaveLen(1))
+	g.Expect(pushData.ConfigChanges[0].ConfigID).To(gomega.Equal(agentID.String()))

-	if len(pushData.ConfigScrapers) != 1 {
-		t.Fatalf("expected 1 config scraper, got %d", len(pushData.ConfigScrapers))
-	}
-	if pushData.ConfigScrapers[0].ID == uuid.Nil {
-		t.Error("system scraper should have been filtered out")
-	}
+	g.Expect(pushData.ConfigScrapers).To(gomega.HaveLen(1))
+	g.Expect(pushData.ConfigScrapers[0].ID).ToNot(gomega.Equal(uuid.Nil))

-	if len(pushData.ConfigItemsLastScrapedTime) != 1 {
-		t.Fatalf("expected 1 last scraped time entry, got %d", len(pushData.ConfigItemsLastScrapedTime))
-	}
-	if pushData.ConfigItemsLastScrapedTime[0].ConfigID != agentID {
-		t.Errorf("expected last scraped time config_id to be %s, got %s", agentID, pushData.ConfigItemsLastScrapedTime[0].ConfigID)
-	}
+	g.Expect(pushData.ConfigItemsLastScrapedTime).To(gomega.HaveLen(1))
+	g.Expect(pushData.ConfigItemsLastScrapedTime[0].ConfigID).To(gomega.Equal(agentID))
 }

As per coding guidelines, **/*_test.go: Always use github.com/onsi/gomega package for assertions in test files; when using gomega with native go tests, use the gomega.NewWithT(t) approach for assertions.

📝 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 TestAddAgentConfig(t *testing.T) {
agentID := uuid.New()
agent := models.Agent{
ID: agentID,
Name: "test-agent",
}
now := time.Now()
pushData := &PushData{
ConfigItems: []models.ConfigItem{
{
ID: uuid.Nil,
Type: lo.ToPtr("MissionControl::ShouldBeFiltered"),
},
{
ID: uuid.New(),
Type: lo.ToPtr("MissionControl::Agent"),
},
},
ConfigChanges: []models.ConfigChange{
{ConfigID: uuid.Nil.String()},
},
ConfigScrapers: []models.ConfigScraper{
{ID: uuid.Nil},
{ID: uuid.New()},
},
ConfigItemsLastScrapedTime: []models.ConfigItemLastScrapedTime{
{ConfigID: uuid.Nil, LastScrapedTime: &now},
},
}
pushData.AddAgentConfig(agent)
// Config item with uuid.Nil ID should be filtered out
if len(pushData.ConfigItems) != 1 {
t.Fatalf("expected 1 config item, got %d", len(pushData.ConfigItems))
}
// MissionControl::Agent item should have ID set to agent ID
ci := pushData.ConfigItems[0]
if ci.ID != agentID {
t.Errorf("expected agent config item ID to be %s, got %s", agentID, ci.ID)
}
if lo.FromPtr(ci.Name) != "test-agent" {
t.Errorf("expected agent config item name to be 'test-agent', got %s", lo.FromPtr(ci.Name))
}
if lo.FromPtr(ci.ScraperID) != uuid.Nil.String() {
t.Errorf("expected scraper_id to be nil UUID, got %s", lo.FromPtr(ci.ScraperID))
}
// Config changes with uuid.Nil config_id should be remapped
if pushData.ConfigChanges[0].ConfigID != agentID.String() {
t.Errorf("expected config change config_id to be %s, got %s", agentID, pushData.ConfigChanges[0].ConfigID)
}
// System scraper should be filtered out
if len(pushData.ConfigScrapers) != 1 {
t.Fatalf("expected 1 config scraper, got %d", len(pushData.ConfigScrapers))
}
if pushData.ConfigScrapers[0].ID == uuid.Nil {
t.Error("system scraper should have been filtered out")
}
// Last scraped time with uuid.Nil config_id should be remapped to agent ID
if len(pushData.ConfigItemsLastScrapedTime) != 1 {
t.Fatalf("expected 1 last scraped time entry, got %d", len(pushData.ConfigItemsLastScrapedTime))
}
if pushData.ConfigItemsLastScrapedTime[0].ConfigID != agentID {
t.Errorf("expected last scraped time config_id to be %s, got %s", agentID, pushData.ConfigItemsLastScrapedTime[0].ConfigID)
}
}
func TestAddAgentConfig(t *testing.T) {
g := gomega.NewWithT(t)
agentID := uuid.New()
agent := models.Agent{
ID: agentID,
Name: "test-agent",
}
now := time.Now()
pushData := &PushData{
ConfigItems: []models.ConfigItem{
{
ID: uuid.Nil,
Type: lo.ToPtr("MissionControl::ShouldBeFiltered"),
},
{
ID: uuid.New(),
Type: lo.ToPtr("MissionControl::Agent"),
},
},
ConfigChanges: []models.ConfigChange{
{ConfigID: uuid.Nil.String()},
},
ConfigScrapers: []models.ConfigScraper{
{ID: uuid.Nil},
{ID: uuid.New()},
},
ConfigItemsLastScrapedTime: []models.ConfigItemLastScrapedTime{
{ConfigID: uuid.Nil, LastScrapedTime: &now},
},
}
pushData.AddAgentConfig(agent)
// Config item with uuid.Nil ID should be filtered out
g.Expect(pushData.ConfigItems).To(gomega.HaveLen(1))
ci := pushData.ConfigItems[0]
g.Expect(ci.ID).To(gomega.Equal(agentID))
g.Expect(lo.FromPtr(ci.Name)).To(gomega.Equal("test-agent"))
g.Expect(lo.FromPtr(ci.ScraperID)).To(gomega.Equal(uuid.Nil.String()))
// Config changes with uuid.Nil config_id should be remapped
g.Expect(pushData.ConfigChanges).To(gomega.HaveLen(1))
g.Expect(pushData.ConfigChanges[0].ConfigID).To(gomega.Equal(agentID.String()))
// System scraper should be filtered out
g.Expect(pushData.ConfigScrapers).To(gomega.HaveLen(1))
g.Expect(pushData.ConfigScrapers[0].ID).ToNot(gomega.Equal(uuid.Nil))
// Last scraped time with uuid.Nil config_id should be remapped to agent ID
g.Expect(pushData.ConfigItemsLastScrapedTime).To(gomega.HaveLen(1))
g.Expect(pushData.ConfigItemsLastScrapedTime[0].ConfigID).To(gomega.Equal(agentID))
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@upstream/upstream_test.go` around lines 15 - 86, TestAddAgentConfig uses
native t.Errorf/t.Fatalf assertions; replace them with Gomega by creating g :=
gomega.NewWithT(t) at start of the test and convert each assertion to
g.Expect(...) style—e.g., check lengths with
g.Expect(len(pushData.ConfigItems)).To(Equal(1)), equality checks for IDs/names
with g.Expect(...).To(Equal(...)), and ensure boolean conditions use appropriate
matchers. Also add the github.com/onsi/gomega import and remove direct
t.Fatalf/t.Errorf calls, keeping the rest of the test logic (references:
TestAddAgentConfig, pushData.AddAgentConfig, pushData.ConfigItems,
pushData.ConfigChanges, pushData.ConfigScrapers,
pushData.ConfigItemsLastScrapedTime).

Comment on lines +357 to +362
// Update agent's last scraped times with correct config ID
for i, ci := range t.ConfigItemsLastScrapedTime {
if ci.ConfigID == uuid.Nil {
t.ConfigItemsLastScrapedTime[i].ConfigID = agent.ID
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Guard remap to avoid dangling config_id assignments.

Line 357 remaps every nil ConfigID to agent.ID without verifying that an agent config item is present in t.ConfigItems. That can produce invalid config_items_last_scraped_time references in partial payloads.

Suggested fix
 	// Update agent's last scraped times with correct config ID
+	agentConfigID := uuid.Nil
+	for _, item := range t.ConfigItems {
+		if lo.FromPtr(item.Type) == "MissionControl::Agent" {
+			agentConfigID = item.ID
+			break
+		}
+	}
+
 	for i, ci := range t.ConfigItemsLastScrapedTime {
-		if ci.ConfigID == uuid.Nil {
-			t.ConfigItemsLastScrapedTime[i].ConfigID = agent.ID
+		if ci.ConfigID == uuid.Nil && agentConfigID != uuid.Nil {
+			t.ConfigItemsLastScrapedTime[i].ConfigID = agentConfigID
 		}
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@upstream/upstream.go` around lines 357 - 362, Only remap nil ConfigID entries
in t.ConfigItemsLastScrapedTime when there is actually an agent config item in
t.ConfigItems; before the loop over t.ConfigItemsLastScrapedTime, scan
t.ConfigItems for a config item matching the current agent (e.g. check for
ci.AgentID == agent.ID or whichever field links config items to the agent) and
set a boolean like hasAgentConfig; then perform the existing remap
(t.ConfigItemsLastScrapedTime[i].ConfigID = agent.ID) only if hasAgentConfig is
true, leaving entries untouched otherwise to avoid creating dangling config_id
references.

@moshloop moshloop merged commit f858566 into main Apr 10, 2026
16 checks passed
@moshloop moshloop deleted the agent-upstream-config-fix branch April 10, 2026 08:25
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