Skip to content

SDKS-4694: Update PasswordCollector to handle nested Password policies.#190

Merged
vibhorgoswami merged 2 commits into
developfrom
SDKS-4694
Apr 29, 2026
Merged

SDKS-4694: Update PasswordCollector to handle nested Password policies.#190
vibhorgoswami merged 2 commits into
developfrom
SDKS-4694

Conversation

@vibhorgoswami
Copy link
Copy Markdown
Contributor

@vibhorgoswami vibhorgoswami commented Apr 23, 2026

JIRA Ticket

SDKS-4694

Description

Matches the iOS implementation of PasswordPolicy verify component for retrieving the password policy. Also adds a backup option to retrieve the password policy from the global scope if the internal password_verify component is not available.

Summary by CodeRabbit

  • Refactor

    • Improved password policy handling and initialization for more reliable password validation.
  • New Features

    • Expanded sample/test forms with additional field types and updated labels.
    • Added interactive test flow templates (form selection and success screens) to exercise form rendering and submission.

@vibhorgoswami vibhorgoswami self-assigned this Apr 23, 2026
@vibhorgoswami vibhorgoswami requested a review from witrisna April 23, 2026 19:12
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 23, 2026

📝 Walkthrough

Walkthrough

PasswordCollector gained an init(input: JsonObject) override and a PASSWORD_POLICY constant; its passwordPolicy() lazy init was simplified to decode directly from provided input (falling back to continueNode.input["passwordPolicy"]). Tests and Android test fixtures were updated to use the new initialization shape.

Changes

Cohort / File(s) Summary
PasswordCollector Implementation
davinci/src/main/kotlin/com/pingidentity/davinci/collector/PasswordCollector.kt
Adds JsonObject import and PASSWORD_POLICY constant; implements override fun init(input: JsonObject): ValidatedCollector; simplifies passwordPolicy() lazy initialization to decode directly from collector input / continueNode.input["passwordPolicy"] and removes prior nested form/component traversal.
Unit tests — PasswordCollector
davinci/src/test/kotlin/com/pingidentity/davinci/PasswordCollectorTest.kt
Refactors tests to initialize the collector via init(...) with a password-policy-shaped JSON input; replaces coEvery stubs with every; updates assertions to check collector fields populated from init; retains regression tests for fallback behavior when top-level passwordPolicy is (missing
Android test flow JSON
davinci/src/androidTest/kotlin/com/pingidentity/davinci/Form-Field-Tests-Flow.json
Replaces prior test flow JSON with a new “Form Field Tests” flow: new execution graph (start trigger, variables save, function routes, multiple PingOne Forms render nodes), added settings (CSP, CSS, favicon), embedded templates and two concrete stored forms, and new flow variables.
Android tests — FormFields
davinci/src/androidTest/kotlin/com/pingidentity/davinci/FormFieldsTest.kt
Introduces companion-object index constants to replace hardcoded collector indices; updates tests to use these constants and asserts an additional label collector ("rich-text").

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • spetrov
  • rodrigoareis

Poem

"I nibble code and hop with glee, 🐇
Init brought order, simpler and free,
Policies found where inputs lie,
Tests aligned — I give a sigh,
Hooray, a build that passes me!"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.39% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: updating PasswordCollector to handle nested password policies, which aligns with the primary modifications in the source code.
Description check ✅ Passed The description includes all required template sections (JIRA Ticket and Description) with relevant details about matching iOS implementation and adding fallback logic.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch SDKS-4694

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

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

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

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 23, 2026

Codecov Report

❌ Patch coverage is 66.66667% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 44.04%. Comparing base (a8272e6) to head (7684b2b).
⚠️ Report is 1 commits behind head on develop.

Files with missing lines Patch % Lines
...ingidentity/davinci/collector/PasswordCollector.kt 66.66% 0 Missing and 2 partials ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##             develop     #190      +/-   ##
=============================================
+ Coverage      43.98%   44.04%   +0.05%     
- Complexity      1231     1233       +2     
=============================================
  Files            306      306              
  Lines           9255     9250       -5     
  Branches        1326     1321       -5     
=============================================
+ Hits            4071     4074       +3     
  Misses          4638     4638              
+ Partials         546      538       -8     
Flag Coverage Δ
integration-tests 28.57% <50.00%> (+0.03%) ⬆️
unit-tests 25.04% <66.66%> (+0.01%) ⬆️

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.

@vibhorgoswami
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 24, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

🧹 Nitpick comments (1)
davinci/src/test/kotlin/com/pingidentity/davinci/PasswordCollectorTest.kt (1)

183-216: Add a regression test for missing continueNode injection.

Current fallback tests always set collector.continueNode. Add one test where field policy is absent and continueNode is never set, so this crash path is covered.

🧪 Suggested test case
+    `@Test`
+    fun `no validation errors when no passwordPolicy and continueNode is not injected`() {
+        val collector = PasswordCollector()
+        collector.init(buildJsonObject {
+            put("type", "PASSWORD_VERIFY")
+            put("key", "user.password")
+        })
+        collector.value = "any"
+        assertEquals(emptyList(), collector.validate())
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@davinci/src/test/kotlin/com/pingidentity/davinci/PasswordCollectorTest.kt`
around lines 183 - 216, The tests assume continueNode is always injected which
misses the crash path when PasswordCollector.continueNode is null; add a new
unit test that initializes PasswordCollector via init(...) without adding a
passwordPolicy to the field or global input and does NOT set
collector.continueNode, then call collector.value = "any" and assert that
collector.validate() returns an empty list (no exception). Reference
PasswordCollector, init, continueNode, value and validate to locate where to add
this regression test.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@davinci/src/main/kotlin/com/pingidentity/davinci/collector/PasswordCollector.kt`:
- Around line 61-66: In the passwordPolicy() function update the JSON
deserialization call to use an explicit type parameter: change
json.decodeFromJsonElement(it) to json.decodeFromJsonElement<PasswordPolicy>(it)
so the PasswordPolicy type is enforced (reference: function passwordPolicy(),
symbol json.decodeFromJsonElement).

---

Nitpick comments:
In `@davinci/src/test/kotlin/com/pingidentity/davinci/PasswordCollectorTest.kt`:
- Around line 183-216: The tests assume continueNode is always injected which
misses the crash path when PasswordCollector.continueNode is null; add a new
unit test that initializes PasswordCollector via init(...) without adding a
passwordPolicy to the field or global input and does NOT set
collector.continueNode, then call collector.value = "any" and assert that
collector.validate() returns an empty list (no exception). Reference
PasswordCollector, init, continueNode, value and validate to locate where to add
this regression test.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: db76fa1b-d203-4594-9303-229f624deac8

📥 Commits

Reviewing files that changed from the base of the PR and between a8272e6 and 848ed8a.

📒 Files selected for processing (2)
  • davinci/src/main/kotlin/com/pingidentity/davinci/collector/PasswordCollector.kt
  • davinci/src/test/kotlin/com/pingidentity/davinci/PasswordCollectorTest.kt

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LGTM

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@davinci/src/androidTest/kotlin/com/pingidentity/davinci/Form-Field-Tests-Flow.json`:
- Around line 663-677: The fixture JSON is serializing user.password as a plain
string so Form.parse() passes formData.value[collector.id()] directly into
collector.init(...), never exercising PasswordCollector.init()'s nested
passwordPolicy or the global fallback; update at least one PASSWORD_VERIFY path
in the Form-Field-Tests-Flow.json to include a real passwordPolicy payload
(nested JSON structure matching what PasswordCollector.init() expects) for the
customForm node that maps to collector.id(), and add another case that omits the
nested policy to exercise the global-scope fallback so both branches of
PasswordCollector.init() are covered by Android tests.
- Around line 479-480: The Agreement Test button reuses id="btnErrorNode",
causing duplicate DOM ids; update the Agreement Test button's id to a unique
value (e.g., id="btnAgreementTest") in the customHTML block so it no longer
collides with the Error Node button; ensure the new id is only applied to the
Agreement Test button and update any test selectors that referenced the old id
if present (other related ids to locate in the diff: btnFormFields,
btnFormValidation, btnErrorNode).
- Around line 1257-1262: The JSON includes duplicate loopback branches that wire
multiValueSourceId "vgii63uk2p" back into node "nhsnrl9trd" multiple times (see
edge id "0rv5kwiqir" with source "nhsnrl9trd" and target "6ivnxxgt2j"); remove
the extra three loopback edges so "vgii63uk2p" only appears once on the true
"Form Fields" render path and not on eval-to-nhsnrl9trd loopbacks, and apply the
same cleanup for the repeated occurrences in the later block (around the
1489-1604 region) to make the selection graph deterministic.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d2a3f5d3-3071-43f3-b344-c76e488ce54e

📥 Commits

Reviewing files that changed from the base of the PR and between 848ed8a and 7684b2b.

📒 Files selected for processing (2)
  • davinci/src/androidTest/kotlin/com/pingidentity/davinci/Form-Field-Tests-Flow.json
  • davinci/src/androidTest/kotlin/com/pingidentity/davinci/FormFieldsTest.kt

Comment on lines +479 to +480
"customHTML": {
"value": "<div\n class=\"bg-light d-flex flex-column justify-content-center align-items-center position-absolute top-0 start-0 bottom-0 end-0 overflow-auto\">\n <div style=\"max-width: 400px; min-width: 400px; width: 100%\">\n <div class=\"card shadow mb-5\">\n <div class=\"card-body p-5 d-flex flex-column\">\n <img class=\"companyLogo align-self-center mb-5\" alt=\"\ncompanyName {{global.variables.companyName}}\n✕\" />\n <h1 class=\"text-center mb-4\">S</h1>\n <p class=\"text-muted text-center\">Select Test Form</p>\n <p class=\"text-danger mdi mdi-alert-circle\" data-id=\"feedback\" data-skcomponent=\"skerror\"></p>\n <form id=\"success\" data-id=\"success\">\n <div class=\"d-flex flex-column\">\n <button data-id=\"button\" type=\"submit\" class=\"btn btn-primary mb-3\" data-skcomponent=\"skbutton\"\n data-skbuttontype=\"form-submit\" data-skform=\"success\" id=\"btnFormFields\"\n data-skbuttonvalue=\"Form Fields\">\n Form Fields\n </button>\n <div class=\"d-flex flex-column\">\n <button data-id=\"button\" class=\"btn btn-link\" data-skcomponent=\"skbutton\"\n data-skbuttontype=\"form-submit\" data-skform=\"success\" id=\"btnFormValidation\"\n data-skbuttonvalue=\"Form Validation\">\n Form Validation\n </button>\n </div>\n <div class=\"d-flex flex-column\">\n <button data-id=\"button\" class=\"btn btn-link\" data-skcomponent=\"skbutton\"\n data-skbuttontype=\"form-submit\" data-skform=\"success\" id=\"btnErrorNode\"\n data-skbuttonvalue=\"Error Node\">\n Error Node\n </button>\n </div>\n <div class=\"d-flex flex-column\">\n <button data-id=\"button\" class=\"btn btn-link\" data-skcomponent=\"skbutton\"\n data-skbuttontype=\"form-submit\" data-skform=\"success\" id=\"btnErrorNode\"\n data-skbuttonvalue=\"Agreement Test\">\n Agreement Test\n </button>\n </div>\n </form>\n </div>\n </div>\n </div>\n</div>"
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 | 🟡 Minor

Give the Agreement button a unique HTML id.

Both the "Error Node" and "Agreement Test" buttons use id="btnErrorNode". Duplicate ids make the template invalid DOM and can break selector-based UI automation against this page.

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

In
`@davinci/src/androidTest/kotlin/com/pingidentity/davinci/Form-Field-Tests-Flow.json`
around lines 479 - 480, The Agreement Test button reuses id="btnErrorNode",
causing duplicate DOM ids; update the Agreement Test button's id to a unique
value (e.g., id="btnAgreementTest") in the customHTML block so it no longer
collides with the Error Node button; ensure the new id is only applied to the
Agreement Test button and update any test selectors that referenced the old id
if present (other related ids to locate in the diff: btnFormFields,
btnFormValidation, btnErrorNode).

Comment on lines +663 to +677
"formData": {
"value": [
{
"key": "user.username",
"value": "[\n {\n \"children\": [\n {\n \"text\": \"default username\"\n }\n ]\n }\n]"
},
{
"key": "user.email",
"value": "[\n {\n \"children\": [\n {\n \"text\": \"default email\"\n }\n ]\n }\n]"
},
{
"key": "user.password",
"value": "[\n {\n \"children\": [\n {\n \"text\": \"default password\"\n }\n ]\n }\n]"
}
]
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

Add a real passwordPolicy payload to at least one PASSWORD_VERIFY path.

Both customForm nodes still serialize user.password as a plain value. In davinci/src/main/kotlin/com/pingidentity/davinci/collector/Form.kt:29-60, Form.parse() passes formData.value[collector.id()] straight into collector.init(...), so this fixture never drives the new nested-policy path in PasswordCollector.init(). There is also no companion case here that exercises the global-scope fallback. As written, the PR’s core behavior can regress without any Android test catching it.

Also applies to: 901-915

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

In
`@davinci/src/androidTest/kotlin/com/pingidentity/davinci/Form-Field-Tests-Flow.json`
around lines 663 - 677, The fixture JSON is serializing user.password as a plain
string so Form.parse() passes formData.value[collector.id()] directly into
collector.init(...), never exercising PasswordCollector.init()'s nested
passwordPolicy or the global fallback; update at least one PASSWORD_VERIFY path
in the Form-Field-Tests-Flow.json to include a real passwordPolicy payload
(nested JSON structure matching what PasswordCollector.init() expects) for the
customForm node that maps to collector.id(), and add another case that omits the
nested policy to exercise the global-scope fallback so both branches of
PasswordCollector.init() are covered by Android tests.

Comment on lines +1257 to +1262
"data": {
"id": "0rv5kwiqir",
"source": "nhsnrl9trd",
"target": "6ivnxxgt2j",
"multiValueSourceId": "vgii63uk2p"
},
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

Remove the extra "Form Fields" loopback branches.

multiValueSourceId: "vgii63uk2p" is wired four times: once to the actual "Form Fields" render path and three more times back into nhsnrl9trd through eval nodes. That makes the selection graph ambiguous and potentially order-dependent instead of deterministic.

Also applies to: 1489-1604

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

In
`@davinci/src/androidTest/kotlin/com/pingidentity/davinci/Form-Field-Tests-Flow.json`
around lines 1257 - 1262, The JSON includes duplicate loopback branches that
wire multiValueSourceId "vgii63uk2p" back into node "nhsnrl9trd" multiple times
(see edge id "0rv5kwiqir" with source "nhsnrl9trd" and target "6ivnxxgt2j");
remove the extra three loopback edges so "vgii63uk2p" only appears once on the
true "Form Fields" render path and not on eval-to-nhsnrl9trd loopbacks, and
apply the same cleanup for the repeated occurrences in the later block (around
the 1489-1604 region) to make the selection graph deterministic.

@vibhorgoswami vibhorgoswami merged commit 4351962 into develop Apr 29, 2026
34 of 36 checks passed
@vibhorgoswami vibhorgoswami deleted the SDKS-4694 branch April 29, 2026 20:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants