Skip to content

refactor: use environment names instead of IDs for rule matching#58

Merged
calthejuggler merged 4 commits intomainfrom
feature/assignment-rules-updates
Apr 15, 2026
Merged

refactor: use environment names instead of IDs for rule matching#58
calthejuggler merged 4 commits intomainfrom
feature/assignment-rules-updates

Conversation

@calthejuggler
Copy link
Copy Markdown
Contributor

@calthejuggler calthejuggler commented Apr 15, 2026

Description

Switch assignment rule environment matching from numeric environment_id (from API response) to string environment names (from client). This aligns the SDK with how the collector handles environment-scoped rules and simplifies the matching logic.

Solution

  • Environment matching by name: evaluateRules in matcher.ts now accepts environmentName: string | null instead of environmentId: number | null, matching rule environments by name string rather than numeric ID.
  • Reordered rule/audience evaluation: Rules are now evaluated before audience checks. audienceMismatch is only set when no rule override applies, preventing incorrect flags when a rule match should take precedence.
  • System attributes support: Added application, environment, and app_version as automatically-injected attributes when includeSystemAttributes option is enabled, allowing rules to match on these without manual attribute setting.
  • Removed environment_id from ContextData: The environment is now resolved from the client via getEnvironment() rather than from the API response payload.

Other Changes

  • Removed numeric environment edge-case tests (fractional IDs, environmentId 0) that no longer apply with string-based matching.
  • Changed some mockReturnValueOnce calls to mockReturnValue for consistency in tests.

How to Test

npm test

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)

Checklist

  • I have added tests that prove my fix is effective or that my feature works
  • I have made a demo video for the frontend changes (If applicable)
  • New and existing unit tests pass locally with my changes

Summary by CodeRabbit

  • Refactor

    • Environment matching now uses environment names (strings) instead of numeric IDs.
    • System attributes (application, environment, app version) can be optionally included in rule evaluation.
  • Bug Fixes

    • Improved audience-mismatch evaluation in strict mode for more accurate exposure outcomes.
    • Clarified assignment logic to ensure correct full-on variant assignments.
  • Tests

    • Expanded coverage for name-based environment matching, strict-mode audience scenarios and system-attribute gating.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 15, 2026

Walkthrough

The PR migrates environment scoping from numeric IDs to environment names across core logic and tests. Context now tracks _environmentName (from the client) instead of _environmentId, injects system attributes (application, environment, app_version) when requested, and alters assignment/audience-evaluation control flow to run after rule-variant handling and only when no rule override is present. AudienceMatcher.evaluateRules now accepts an environment name and matches rules by name. Tests were updated to use environment names and to cover strict-mode and system-attribute cases.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • mario-silva
  • marcio-absmartly

Poem

🐇 In gardens of code I hop with glee,

environment names replace ID, you see,
System attributes tucked in tight,
Rules now match by name and light,
A small carrot dance for CI's spree 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately summarizes the main change: switching from numeric environment IDs to string environment names for rule matching, which is the core refactoring throughout all modified files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ 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 feature/assignment-rules-updates

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ast-grep (0.42.1)
src/__tests__/context.test.js

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

@calthejuggler calthejuggler marked this pull request as ready for review April 15, 2026 15:46
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/context.ts (1)

487-525: ⚠️ Potential issue | 🟠 Major

Include injected system attributes in the assignment cache key.

audienceMatches() now depends on application / environment / app_version via _getAttributesMap(), but this branch only re-runs when this._attrsSeq or ruleKey changes. this._attrsSeq only tracks context.attribute() calls, and ruleKey only captures assignmentRules + _environmentName, so a changed getApplication() / app_version leaves a stale assignment cached after refresh(). Please fold a stable snapshot of the injected system attributes into this invalidation check before reusing assignment.

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

In `@src/context.ts` around lines 487 - 525, The assignment cache is not
invalidated when injected system attributes change; update audienceMatches to
incorporate a stable snapshot key of injected system attributes (e.g.,
application, environment/_environmentName, app_version) when deciding to reuse
an Assignment: compute a deterministic systemAttrsKey from
this._getAttributesMap() or from
getApplication()/this._environmentName/app_version, compare it to a stored
assignment.systemAttrsKey (or add that field to Assignment), and treat a
mismatch like ruleKeyChanged/attrsSeq change so the method recomputes
ruleVariant/audience and updates assignment.systemAttrsKey along with
assignment.ruleKey and assignment.attrsSeq before returning the cached
assignment.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/context.ts`:
- Around line 461-469: The environment attribute is left undefined here while
_init() coerces client.getEnvironment() to null; update the injection to
normalize the value the same way by assigning attrs["environment"] =
client.getEnvironment() ?? null when this._opts.includeSystemAttributes is true
(locate the block using this._sdk.getClient(), app = client.getApplication(),
and attrs["application"]), and make the same null-coercion change in
_buildAttributes() so rule vars and serialized system attributes receive null
instead of undefined.

---

Outside diff comments:
In `@src/context.ts`:
- Around line 487-525: The assignment cache is not invalidated when injected
system attributes change; update audienceMatches to incorporate a stable
snapshot key of injected system attributes (e.g., application,
environment/_environmentName, app_version) when deciding to reuse an Assignment:
compute a deterministic systemAttrsKey from this._getAttributesMap() or from
getApplication()/this._environmentName/app_version, compare it to a stored
assignment.systemAttrsKey (or add that field to Assignment), and treat a
mismatch like ruleKeyChanged/attrsSeq change so the method recomputes
ruleVariant/audience and updates assignment.systemAttrsKey along with
assignment.ruleKey and assignment.attrsSeq before returning the cached
assignment.
🪄 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: 24af86e7-87fc-42db-b4ec-18fc0c40aff1

📥 Commits

Reviewing files that changed from the base of the PR and between ddf6ae7 and 99a4a7f.

📒 Files selected for processing (4)
  • src/__tests__/context.test.js
  • src/__tests__/matcher.test.js
  • src/context.ts
  • src/matcher.ts

Comment thread src/context.ts
Copy link
Copy Markdown

@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)
src/context.ts (1)

461-469: Consider extracting the shared app_version condition.

The condition for including app_version (lines 466–468) is duplicated in _buildAttributes() (lines 871–873). Extracting this to a helper method would reduce duplication and ensure consistent behaviour if the logic changes.

♻️ Optional refactor to extract shared logic
private _shouldIncludeAppVersion(version: string | number): boolean {
  return (typeof version === "string" && version.length > 0) || (typeof version === "number" && version > 0);
}

Then use it in both methods:

-if ((typeof app.version === "string" && app.version.length > 0) || (typeof app.version === "number" && app.version > 0)) {
+if (this._shouldIncludeAppVersion(app.version)) {
   attrs["app_version"] = app.version;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/context.ts` around lines 461 - 469, Extract the duplicated app_version
check into a single helper on the Context class (e.g.
_shouldIncludeAppVersion(version: string | number): boolean) and replace the
inline condition in both the includeSystemAttributes block (where
attrs["app_version"] is set) and the _buildAttributes() method to call this
helper; ensure the helper returns true for non-empty strings or numbers > 0 so
behavior stays identical and update both sites to use the new method.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/context.ts`:
- Around line 461-469: Extract the duplicated app_version check into a single
helper on the Context class (e.g. _shouldIncludeAppVersion(version: string |
number): boolean) and replace the inline condition in both the
includeSystemAttributes block (where attrs["app_version"] is set) and the
_buildAttributes() method to call this helper; ensure the helper returns true
for non-empty strings or numbers > 0 so behavior stays identical and update both
sites to use the new method.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2bf163fc-caae-45e1-905e-03fc0617d467

📥 Commits

Reviewing files that changed from the base of the PR and between ddf6ae7 and 57ce050.

📒 Files selected for processing (4)
  • src/__tests__/context.test.js
  • src/__tests__/matcher.test.js
  • src/context.ts
  • src/matcher.ts

@joalves joalves self-requested a review April 15, 2026 16:26
Copy link
Copy Markdown

@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)
src/__tests__/context.test.js (1)

2575-2637: Add a regression test for environment normalisation to null.

Lines 2575–2637 cover system-attribute matching well for "production", but they do not protect the recent undefined→null normalisation. Please add one case with client.getEnvironment returning undefined and assert the emitted/matched environment attribute is null.

🧪 Suggested test addition
+		it("should normalise environment system attribute to null when getEnvironment returns undefined", () => {
+			client.getEnvironment.mockReturnValueOnce(undefined);
+			const envNullRuleResponse = buildRulesResponse({
+				assignmentRules: JSON.stringify({
+					rules: [
+						{
+							name: "Null Environment",
+							type: "assign",
+							conditions: { and: [{ eq: [{ var: "environment" }, { value: null }] }] },
+							environments: [],
+							variant: 1,
+						},
+					],
+				}),
+			});
+			const optionsWithSystemAttrs = {
+				publishDelay: -1,
+				refreshPeriod: 0,
+				includeSystemAttributes: true,
+			};
+			const context = new Context(sdk, optionsWithSystemAttrs, contextParams, envNullRuleResponse);
+			expect(context.treatment("exp_test_abc")).toEqual(1);
+		});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/__tests__/context.test.js` around lines 2575 - 2637, Add a regression
test that ensures environment undefined is normalised to null before rule
evaluation: in the test suite create a rule that matches environment === null
(use buildRulesResponse with a rule whose condition compares { var:
"environment" } to { value: null }), stub the SDK client getEnvironment to
return undefined (or set contextParams/environment to undefined) and construct
Context with includeSystemAttributes: true (as in optionsWithSystemAttrs), then
call Context.treatment("exp_test_abc") and assert it matches the rule variant
(1). Place this alongside the existing environment tests and reference Context
and treatment to locate where to add the new case.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/__tests__/context.test.js`:
- Around line 2575-2637: Add a regression test that ensures environment
undefined is normalised to null before rule evaluation: in the test suite create
a rule that matches environment === null (use buildRulesResponse with a rule
whose condition compares { var: "environment" } to { value: null }), stub the
SDK client getEnvironment to return undefined (or set contextParams/environment
to undefined) and construct Context with includeSystemAttributes: true (as in
optionsWithSystemAttrs), then call Context.treatment("exp_test_abc") and assert
it matches the rule variant (1). Place this alongside the existing environment
tests and reference Context and treatment to locate where to add the new case.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9ace070e-d8c3-48c8-bd39-51897c74833f

📥 Commits

Reviewing files that changed from the base of the PR and between 57ce050 and 2ced41c.

📒 Files selected for processing (3)
  • src/__tests__/context.test.js
  • src/__tests__/matcher.test.js
  • src/context.ts
✅ Files skipped from review due to trivial changes (1)
  • src/context.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/tests/matcher.test.js

@calthejuggler calthejuggler merged commit 90a5206 into main Apr 15, 2026
2 checks passed
@calthejuggler calthejuggler deleted the feature/assignment-rules-updates branch April 15, 2026 16:29
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