Skip to content

Fix ValidatePrivateFields: UpperFirst corrupts unexported embedded struct field paths#331

Merged
inhere merged 2 commits into
masterfrom
copilot/fix-validate-private-fields-issue
Mar 25, 2026
Merged

Fix ValidatePrivateFields: UpperFirst corrupts unexported embedded struct field paths#331
inhere merged 2 commits into
masterfrom
copilot/fix-validate-private-fields-issue

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 24, 2026

ValidatePrivateFields = true had no effect because strutil.UpperFirst() was unconditionally applied in TryGet and Set, turning paths like foo.Field1 into Foo.Field1. Since the embedded struct is registered as foo (lowercase), FieldByName("Foo") always failed silently — making validation behave as if the field didn't exist regardless of its value.

The existing test masked this: it asserted hasError == true with an invalid value, which passed — but only because the field was invisible to the validator, not because the rule fired. Setting a valid value still produced errors.

Changes

  • data_source.goTryGet and Set: apply UpperFirst only when the field is not already present in d.fieldNames. Fields registered during parseRulesFromTag (including private embedded struct fields) retain their original casing; exported field name normalization (e.g. "name""Name") is preserved as a fallback.

  • data_source_test.go — Extend TestValidatePrivateFieldsWhenTrue to assert that validation passes when the embedded field holds a valid value — the case that was previously broken:

// Field1 = 4 violates min:5 — must fail
v := Struct(barz)
v.Validate()
assert.Equal(t, v.hasError, true)

// Field1 = 5 satisfies all rules — must pass (was broken before fix)
barz.foo.Field1 = 5
v = Struct(barz)
v.Validate()
assert.Equal(t, v.hasError, false)
Original prompt

This section details on the original issue you should resolve

<issue_title>ValidatePrivateFields 的配置没有效果</issue_title>
<issue_description>我尝试了这个配置ValidatePrivateFields,但其实并没有效果。

然后我检查了源代码里面的单元测试 TestValidatePrivateFieldsWhenTrue,但这个单元测试其实有问题的。

assert.Equal(t, v.hasError, true)

这一行断言实际上存在问题,我在debug时发现,断言失败是因为根本没有拿到字段的值,由于

field = strutil.UpperFirst(field)

这一行的存在,导致拿字段的值的Path是Foo.Field1而不是foo.Field1

所以在原有的单测下面加

	barz.Field1 = 5
	v = Struct(barz)
	v.Validate()

	assert.Equal(t, v.hasError, false)

期望应该是没有错误,但还是有错误。

是否把

field = strutil.UpperFirst(field)

这行删掉即可?</issue_description>

<agent_instructions>找到问题并提供修复PR</agent_instructions>

Comments on the Issue (you are @copilot in this section)


⌨️ Start Copilot coding agent tasks without leaving your editor — available in VS Code, Visual Studio, JetBrains IDEs and Eclipse.

Copilot AI changed the title [WIP] Fix ValidatePrivateFields configuration issue in tests Fix ValidatePrivateFields: UpperFirst corrupts unexported embedded struct field paths Mar 24, 2026
Copilot AI requested a review from inhere March 24, 2026 13:57
@inhere inhere marked this pull request as ready for review March 25, 2026 01:51
Copilot AI review requested due to automatic review settings March 25, 2026 01:51
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes struct field-path normalization so ValidatePrivateFields = true can correctly access validation rules on unexported embedded struct fields (e.g. foo.Field1) without corrupting the path casing.

Changes:

  • Adjust StructData.TryGet / StructData.Set to apply UpperFirst only as a fallback when the field path isn’t already registered.
  • Extend TestValidatePrivateFieldsWhenTrue to assert the “valid value passes” case that previously failed.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
data_source.go Makes path normalization conditional to preserve original casing for registered (including private embedded) field paths.
data_source_test.go Adds a regression assertion ensuring validation passes when the embedded field value is valid.

Comment thread data_source.go
Comment on lines +640 to 645
// Only uppercase if the field is not already registered with its original casing.
// This mirrors the same fix in TryGet for private embedded struct fields.
if _, ok := d.fieldNames[field]; !ok {
field = strutil.UpperFirst(field)
}
if !d.HasField(field) { // field not found
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

Set can now skip UpperFirst for registered embedded-field paths (e.g. foo.Field1), but the rest of Set does not handle fieldAtAnonymous (the switch case is empty). If d.fieldNames[field] is fieldAtAnonymous, fv remains an invalid reflect.Value and removeValuePtr(fv) will panic. Consider handling fieldAtAnonymous the same as fieldAtSubStruct (walk the path and select the nested field), or otherwise return a controlled error instead of leaving fv invalid.

Copilot uses AI. Check for mistakes.
Comment thread data_source_test.go
Comment on lines 283 to +290
v.Validate()

assert.Equal(t, v.hasError, true)

// Field1 = 5 satisfies all rules — validation must pass
barz.foo.Field1 = 5
v = Struct(barz)
v.Validate()
assert.Equal(t, v.hasError, false)
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

These assertions pass actual then expected to assert.Equal, which is inconsistent with the surrounding tests that use assert.Equal(t, expected, actual) (e.g. data_source_test.go:260-261, issues_test.go:511). Swapping the argument order (or using assert.True/False) will make assertion failures much easier to interpret.

Copilot uses AI. Check for mistakes.
@inhere inhere merged commit d265741 into master Mar 25, 2026
25 checks passed
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.

ValidatePrivateFields 的配置没有效果

3 participants