Skip to content

Add team label tests to replace TODOs from PR #37208 - #37669

Closed
iansltx with Copilot wants to merge 84 commits into
mainfrom
copilot/add-missing-tests-for-labels
Closed

Add team label tests to replace TODOs from PR #37208#37669
iansltx with Copilot wants to merge 84 commits into
mainfrom
copilot/add-missing-tests-for-labels

Conversation

Copilot AI commented Dec 24, 2025

Copy link
Copy Markdown
Contributor

PR #37208 introduced team labels API but left TODOs for missing test coverage. This PR implements comprehensive tests for team label functionality across datastore, service, and integration layers.

Changes

Datastore tests (server/datastore/mysql/labels_test.go):

  • Team label filtering and isolation validation
  • Team-scoped label spec operations (apply/retrieve)
  • Team label lookups by name/ID with proper filtering
  • Team label deletion with team scope enforcement
  • Host membership validation (team labels only accept hosts from same team)

Service layer tests (server/service/labels_test.go):

  • Authorization matrix for team labels across user roles (global admin/maintainer/observer, team admin/maintainer/observer)
  • Read/write permission validation

Integration tests (server/service/integration_core_test.go):

  • End-to-end team label CRUD via API
  • Team label specs API (apply/retrieve)
  • Team label host assignment with cross-team isolation

All tests follow existing patterns and validate team isolation boundaries.

Code quality improvements (based on automated code review feedback):

  • Consistent error handling using require.NoError(t, err) instead of require.Nil(t, err)
  • Consistent context usage with existing ctx variable instead of context.Background()
  • Removed unused variables and trailing whitespace

Checklist for submitter

  • Added/updated automated tests
  • Where appropriate, automated tests simulate multiple hosts and test for host isolation
Original prompt

in #37208, TODOs were added for missing tests or test cases in two labels_test files and integration_core_test.go. Replace these TODOs with the appropriate tests and create a new PR off the branch.


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Summary by CodeRabbit

  • Tests
    • Added comprehensive test coverage for team-scoped labels, including validation of creation, retrieval, deletion, filtering, and access control scenarios across multiple test suites.

✏️ Tip: You can customize this high-level summary in your review settings.

Turns out, we have a lot to unwind here so this doesn't compile yet
Zed + Opus 4.5. Prompt below:

Try to run `make test` and fix compile errors where the interface no longer matches the implementation until the compile completes. Don't change the associated interfaces, and use commit 5ad3c18 as a guide for the sort of changes that need to be made.
Courtesy Junie. Prompt:

Using PlatformField.tsx as a pattern, give me a text field component (not an input, not editable) for "Team name". Include this in both ManualLabelForm and DynamicLabelForm, showing the component only if a new teamName prop (nullable string, required) is truthy. Pass this property all the way from LabelForm. There, add team_name to ILabel as an optional nullable string, and pass that to the DynamicLabelForm and ManualLabelForm components.

TODO: A bunch of manual fixes, but figured I'd split those into a commit to show what's mine/what the robot did.
… controls on label edit page and manage labels action menu
Used Zed + Opus 4.5 with the prompt:

I'm getting test failures on frontend tests (`make test-js`) because I'm pretty sure real server requests are leaking through HTTP mocks. Fix the issues.

It made some other changes that weren't relevant for fixing the problem, so I reverted those prior to committing.
Still TODO:

* Database query changes
* Filtering individual labels by team (including authorship)
* Filtering associated hosts on edit
Thought I could use this for labels work but I can't
Zed + Opus 4.5. Prompt:

Clean up testLabelsSummary work added in the most recent commit to iterate over a table of test cases rather than duplicating a bunch of test code. Name test cases based on the comments above each subtest.
Zed + Opus 4.5. Prompt:

Infer expectedCount from expectedTeamLabels (expectedCount will always be 5 + len(expectedTeamLabels). Also remove default values from the testCases struct declarations, then inline the testCases variable rather than assigning to a temporary variable only used in the loop.
Still need to move auth checks around, which I'll have to do in part at the data store level so we don't load a label that isn't permitted into memory only to 403 after we grab it.
Copilot AI self-assigned this Dec 24, 2025
fleet-release
fleet-release previously approved these changes Dec 24, 2025
Copilot AI and others added 4 commits December 24, 2025 04:09
Co-authored-by: iansltx <472804+iansltx@users.noreply.github.com>
Co-authored-by: iansltx <472804+iansltx@users.noreply.github.com>
Co-authored-by: iansltx <472804+iansltx@users.noreply.github.com>
Co-authored-by: iansltx <472804+iansltx@users.noreply.github.com>
Copilot AI changed the title [WIP] Add missing tests for labels_test files and integration_core_test.go Add team label tests to replace TODOs from PR #37208 Dec 24, 2025
Copilot AI requested a review from iansltx December 24, 2025 04:22
@iansltx

iansltx commented Dec 24, 2025

Copy link
Copy Markdown
Contributor

@coderabbitai full-review

@coderabbitai

coderabbitai Bot commented Dec 24, 2025

Copy link
Copy Markdown
Contributor

@iansltx: I'll conduct a full review of the PR changes.


🧠 Learnings used
Learnt from: getvictor
Repo: fleetdm/fleet PR: 31726
File: server/datastore/mysql/labels_test.go:2031-2031
Timestamp: 2025-08-08T07:40:05.301Z
Learning: In fleetdm/fleet repository tests (server/datastore/mysql/labels_test.go and similar), using testing.T.Context() is valid because the project targets a recent Go version where testing.T.Context() exists. Do not suggest replacing t.Context() with context.Background() in this codebase.
✅ Actions performed

Full review triggered.

@coderabbitai

coderabbitai Bot commented Dec 24, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

The PR adds comprehensive test coverage for team-scoped labels across the datastore, service integration, and authorization layers, validating label creation, retrieval, deletion, membership updates, and access control for both global and team-specific contexts.

Changes

Cohort / File(s) Summary
Datastore Label Tests
server/datastore/mysql/labels_test.go
Extends multiple test functions (testLabelsListHostsInLabel, testLabelsApplySpecsRoundtrip, testLabelsIDsByName, testLabelsByName, testDeleteLabel, testLabelsSummaryAndListTeamFiltering, testUpdateLabelMembershipByHostIDs, testUpdateLabelMembershipForTransferredHost, testApplyLabelSpecsForSerialUUID, testUpdateLabelMembershipByHostCriteria, testTeamLabels) to validate team-scoped label behavior, including team creation, assignment, filtering with TeamFilter, and access control across global and team-specific contexts.
Service Integration Tests
server/service/integration_core_test.go
Adds new test sections within TestLabels, TestLabelSpecs, and TestAddingRemovingManualLabels to cover team-scoped label creation, retrieval, deletion, dynamic vs. manual label types, and role-based access control for team-labeled hosts.
Service Authorization Tests
server/service/labels_test.go
Extends TestLabelsAuth with team-scoped label scenarios, including mock datastore injection for team label creation/retrieval and comprehensive authorization validation for read/write/delete operations across global and team-specific access levels.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • fleetdm/fleet#37116: Implements backend changes for team-scoped labels (creation, deletion, membership cleanup) across label, host, and team deletion flows that the new tests validate.
  • fleetdm/fleet#37246: Extends team-scoped label behavior with label-assignment validation and check APIs that the new test scenarios exercise and verify.

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding team label tests to replace TODOs from a previous PR (#37208).
Description check ✅ Passed The description provides clear context, lists changes by file with specific functionality covered, and includes a completed testing checklist, though it lacks a Related issue section.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch copilot/add-missing-tests-for-labels

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.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 0

🧹 Nitpick comments (7)
server/service/labels_test.go (1)

215-216: Unused variable: isTeam1Observer.

The variable isTeam1Observer is calculated but never used in the test assertions. Consider removing it since the read/write/delete permission checks don't reference it.

🔎 Proposed cleanup
 			for _, t := range tt.user.Teams {
 				if t.Team.ID == team1.ID {
 					hasTeam1Access = true
 					if t.Role == fleet.RoleMaintainer || t.Role == fleet.RoleAdmin {
 						isTeam1Maintainer = true
-					} else if t.Role == fleet.RoleObserver {
-						isTeam1Observer = true
 					}
 					break
 				}
 			}
server/service/integration_core_test.go (2)

5161-5258: Team label integration tests look good; double‑check host identifiers in manual label spec

The new “Team Labels” and team label specs tests exercise the right behavior:

  • Teams and team‑scoped labels are created via ApplyLabelSpecs.
  • TeamID is asserted both when listing labels/specs and when fetching by ID/name.
  • Label deletion paths for team labels are covered.

One thing to verify: in the manual team label spec for team2, Hosts is populated with fmt.Sprint(team2Hosts[0].ID) (a DB ID string). Elsewhere in this file, manual label membership APIs use UUID/hostname/node key/serial as host identifiers rather than DB IDs. If ApplyLabelSpecs’s host resolution does not explicitly support numeric DB IDs, this could:

  • Fail with a 4xx because the host can’t be found, or
  • Behave differently from the other manual-label entry points.

To keep host identification consistent and robust, consider switching that to a UUID or hostname, e.g. team2Hosts[0].UUID or team2Hosts[0].Hostname, and update expectations if needed.

Also applies to: 5485-5535


13929-13964: Team label add/remove flow is covered; consider extending cross‑team coverage

The added block in TestAddingRemovingManualLabels usefully verifies that:

  • A team‑scoped manual label can be added to a host in that team.
  • Attempting to add the same team label to a global host returns 422 Unprocessable Entity.
  • Removing the team label from the team host works and leaves no residual labels.

This matches the intended isolation semantics for team labels. If you want to harden the tests further, you could optionally add a case that tries to apply a team1 label to a host in a different team and asserts the same rejection path, to ensure cross‑team enforcement is symmetrical.

server/datastore/mysql/labels_test.go (4)

293-424: Inconsistent context usage in the test.

The test defines ctx := context.Background() at line 293 but several subsequent calls still use context.Background() directly (e.g., lines 301, 314, 327, etc.). While this doesn't affect test correctness, it's inconsistent with the pattern established at the start of the test additions.

🔎 Suggested fix for consistency
-	h1, err := db.NewHost(context.Background(), &fleet.Host{
+	h1, err := db.NewHost(ctx, &fleet.Host{

Apply similar changes to h2, h3 creation and other context.Background() calls within this function.


1040-1041: Stale TODO comment should be removed.

The TODO comment at line 1041 states "TODO test team label filtering" but team label filtering tests have now been implemented in this function (lines 1102-1129). Consider removing this stale comment.

🔎 Proposed fix
 func testDeleteLabel(t *testing.T, db *Datastore) {
-	// TODO test team label filtering
-
 	ctx := context.Background()

2037-2037: Stale TODO comment should be removed.

The TODO at line 2037 states "TODO validate team label host validation behavior" but this behavior has now been tested in the additions at lines 2203-2262. Consider removing this stale comment.

🔎 Proposed fix
 func testUpdateLabelMembershipByHostIDs(t *testing.T, ds *Datastore) {
-	// TODO validate team label host validation behavior
-
 	ctx := context.Background()

2820-2821: Empty test function with TODO.

The testSetAsideLabels function is empty with just a TODO comment. If this test case is registered in the test table (line 106), it will pass without testing anything.

Would you like me to help implement this test, or should this be tracked as a separate issue? If intentionally deferred, consider adding a more descriptive TODO comment explaining what needs to be tested.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f939b1b and fcbc7ba.

📒 Files selected for processing (3)
  • server/datastore/mysql/labels_test.go
  • server/service/integration_core_test.go
  • server/service/labels_test.go
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

⚙️ CodeRabbit configuration file

When reviewing SQL queries that are added or modified, ensure that appropriate filtering criteria are applied—especially when a query is intended to return data for a specific entity (e.g., a single host). Check for missing WHERE clauses or incorrect filtering that could lead to incorrect or non-deterministic results (e.g., returning the first row instead of the correct one). Flag any queries that may return unintended results due to lack of precise scoping.

Files:

  • server/service/labels_test.go
  • server/datastore/mysql/labels_test.go
  • server/service/integration_core_test.go
🧠 Learnings (5)
📚 Learning: 2025-08-08T07:40:05.301Z
Learnt from: getvictor
Repo: fleetdm/fleet PR: 31726
File: server/datastore/mysql/labels_test.go:2031-2031
Timestamp: 2025-08-08T07:40:05.301Z
Learning: In fleetdm/fleet repository tests (server/datastore/mysql/labels_test.go and similar), using testing.T.Context() is valid because the project targets a recent Go version where testing.T.Context() exists. Do not suggest replacing t.Context() with context.Background() in this codebase.

Applied to files:

  • server/service/labels_test.go
  • server/datastore/mysql/labels_test.go
  • server/service/integration_core_test.go
📚 Learning: 2025-08-08T07:40:05.301Z
Learnt from: getvictor
Repo: fleetdm/fleet PR: 31726
File: server/datastore/mysql/labels_test.go:2031-2031
Timestamp: 2025-08-08T07:40:05.301Z
Learning: Fleet repo targets Go 1.24.5 (root go.mod), which supports testing.T.Context(). Do not flag usage of t.Context() or suggest replacing it with context.Background() in tests (e.g., server/datastore/mysql/labels_test.go Line 2031 and similar).

Applied to files:

  • server/datastore/mysql/labels_test.go
📚 Learning: 2025-10-03T18:16:11.482Z
Learnt from: MagnusHJensen
Repo: fleetdm/fleet PR: 33805
File: server/service/integration_mdm_test.go:1248-1251
Timestamp: 2025-10-03T18:16:11.482Z
Learning: In server/service/integration_mdm_test.go, the helper createAppleMobileHostThenEnrollMDM(platform string) is exclusively for iOS/iPadOS hosts (mobile). Do not flag macOS model/behavior issues based on changes within this helper; macOS provisioning uses different helpers such as createHostThenEnrollMDM.

Applied to files:

  • server/datastore/mysql/labels_test.go
📚 Learning: 2025-08-08T08:32:31.529Z
Learnt from: getvictor
Repo: fleetdm/fleet PR: 31695
File: server/datastore/mysql/apple_mdm_test.go:132-132
Timestamp: 2025-08-08T08:32:31.529Z
Learning: Datastore.NewMDMWindowsConfigProfile signature is: NewMDMWindowsConfigProfile(ctx context.Context, cp fleet.MDMWindowsConfigProfile, usesFleetVars []string) (*fleet.MDMWindowsConfigProfile, error). Passing nil for usesFleetVars in tests denotes “no Fleet variables referenced” and is used consistently across the repo.

Applied to files:

  • server/datastore/mysql/labels_test.go
📚 Learning: 2025-08-13T18:20:42.136Z
Learnt from: titanous
Repo: fleetdm/fleet PR: 31075
File: tools/redis-tests/elasticache/iam_auth.go:4-10
Timestamp: 2025-08-13T18:20:42.136Z
Learning: For test harnesses and CLI tools in the Fleet codebase, resource cleanup on error paths (like closing connections before log.Fatalf) may not be necessary since the OS handles cleanup when the process exits. These tools prioritize simplicity over defensive programming patterns used in production code.

Applied to files:

  • server/service/integration_core_test.go
🧬 Code graph analysis (3)
server/service/labels_test.go (3)
server/fleet/teams.go (2)
  • Team (69-95)
  • TeamFilter (579-588)
server/mock/datastore_mock.go (2)
  • NewLabelFunc (156-156)
  • LabelFunc (164-164)
server/fleet/labels.go (1)
  • LabelWithTeamName (157-160)
server/datastore/mysql/labels_test.go (2)
server/fleet/teams.go (2)
  • Team (69-95)
  • TeamFilter (579-588)
server/fleet/labels.go (4)
  • LabelSpec (223-234)
  • Label (142-155)
  • LabelType (61-61)
  • LabelMembershipType (95-95)
server/service/integration_core_test.go (2)
server/fleet/hosts.go (1)
  • NewAddHostsToTeamParams (1558-1564)
server/fleet/labels.go (3)
  • LabelSpec (223-234)
  • LabelMembershipTypeManual (102-102)
  • LabelMembershipTypeDynamic (100-100)
🔇 Additional comments (7)
server/service/labels_test.go (2)

121-152: LGTM! Team label setup and mocks are correctly configured.

The team label and mocks are properly set up to support the new permission tests. The mock functions correctly preserve existing test cases while adding the new team-scoped label case.


222-238: Excellent permission test coverage!

The team label permission tests correctly validate the authorization matrix:

  • Global users can read all team labels
  • Global admins/maintainers can modify/delete all team labels
  • Team members can read their team's labels
  • Team admins/maintainers can modify/delete their team's labels
  • Team observers have read-only access to their team's labels

The permission logic is sound and aligns with the PR objectives for validating team isolation boundaries.

server/datastore/mysql/labels_test.go (5)

766-805: LGTM!

The team label specs test correctly validates:

  • Team labels are created and retrieved with proper team filtering
  • Global specs do not include team labels
  • Team1 and Team2 specs are isolated from each other

816-845: LGTM!

The test properly validates:

  • Team labels can be looked up by name with the correct team filter
  • Global filter correctly excludes team labels
  • Label IDs are correctly returned

872-903: LGTM!

The test provides good coverage for LabelsByName with team labels, including validation that label descriptions are preserved and that global filters properly exclude team labels.


1102-1129: LGTM!

The test correctly validates team label deletion behavior:

  • Deletion with global filter fails with NotFoundError (team label not visible in global scope)
  • Deletion with correct team filter succeeds
  • Subsequent lookup confirms the label is deleted

This properly tests the team isolation boundary for delete operations.


2203-2262: LGTM - Critical team isolation test.

This test validates an important security boundary: hosts from one team cannot be added to another team's label. The test correctly:

  1. Creates hosts in separate teams
  2. Creates a team-scoped label
  3. Validates that adding a host from the correct team succeeds
  4. Validates that adding a host from a different team is filtered out (returns empty hostIDs)

This is essential for ensuring team isolation is enforced at the datastore level.

@iansltx

iansltx commented Dec 24, 2025

Copy link
Copy Markdown
Contributor

@copilot take CodeRabbit feedback into account

…ext usage

Co-authored-by: iansltx <472804+iansltx@users.noreply.github.com>

Copilot AI commented Dec 24, 2025

Copy link
Copy Markdown
Contributor Author

@copilot take CodeRabbit feedback into account

I've addressed the CodeRabbit feedback in commit a2a1100:

  • Changed require.Nil(t, err) to require.NoError(t, err) for consistency with modern Go testing practices
  • Replaced context.Background() calls with the existing ctx variable for consistency
  • Removed trailing whitespace
  • Removed unused isTeam1Observer variable

All tests compile successfully.

@codecov

codecov Bot commented Dec 24, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 56.96721% with 210 lines in your changes missing coverage. Please review.
✅ Project coverage is 57.36%. Comparing base (9adfe73) to head (a2a1100).
⚠️ Report is 44 commits behind head on main.

Files with missing lines Patch % Lines
server/datastore/mysql/labels.go 39.54% 115 Missing and 18 partials ⚠️
server/service/labels.go 54.25% 32 Missing and 11 partials ⚠️
frontend/services/entities/labels.ts 56.25% 7 Missing ⚠️
...ManageLabelsPage/LabelsTable/LabelsTableConfig.tsx 40.00% 6 Missing ⚠️
...d/fleetctl/fleetctl/testing_utils/testing_utils.go 72.72% 1 Missing and 2 partials ⚠️
server/service/metrics_labels.go 0.00% 3 Missing ⚠️
...ntend/pages/labels/EditLabelPage/EditLabelPage.tsx 66.66% 2 Missing ⚠️
...s/components/DynamicLabelForm/DynamicLabelForm.tsx 33.33% 1 Missing and 1 partial ⚠️
server/fleet/teams.go 50.00% 1 Missing and 1 partial ⚠️
server/service/global_policies.go 88.88% 1 Missing and 1 partial ⚠️
... and 5 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #37669      +/-   ##
==========================================
- Coverage   65.88%   57.36%   -8.53%     
==========================================
  Files        2360     2361       +1     
  Lines      187285   187383      +98     
  Branches     8006     8017      +11     
==========================================
- Hits       123402   107496   -15906     
- Misses      52605    69479   +16874     
+ Partials    11278    10408     -870     
Flag Coverage Δ
backend 57.89% <55.58%> (-9.86%) ⬇️

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

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

Base automatically changed from 36758-team-labels-api to main December 30, 2025 03:28
@iansltx
iansltx dismissed fleet-release’s stale review December 30, 2025 03:28

The base branch was changed.

@iansltx

iansltx commented Jan 2, 2026

Copy link
Copy Markdown
Contributor

Going to regen this since a lot has changed.

@iansltx iansltx closed this Jan 2, 2026
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