diff --git a/internal/db/common/update_test.go b/internal/db/common/update_test.go index 08e5d1c1d5..4135062305 100644 --- a/internal/db/common/update_test.go +++ b/internal/db/common/update_test.go @@ -417,3 +417,208 @@ func testPublicId(t *testing.T) string { assert.NoError(t, err) return publicId } + +func TestBuildUpdatePaths(t *testing.T) { + type args struct { + fieldValues map[string]interface{} + fieldMask []string + allowZeroFields []string + } + tests := []struct { + name string + args args + wantMasks []string + wantNulls []string + }{ + { + name: "empty-inputs", + args: args{ + fieldValues: map[string]interface{}{}, + fieldMask: []string{}, + allowZeroFields: []string{}, + }, + wantMasks: []string{}, + wantNulls: []string{}, + }, + { + name: "no-changes", + args: args{ + fieldValues: map[string]interface{}{ + "Boolean": true, + "Int": 100, + "String": "hello", + "Float": 1.1, + "Complex": complex(1.1, 1.1), + "ByteSlice": []byte("byte slice"), + "ZeroBoolean": false, + "ZeroInt": 0, + "ZeroString": "", + "ZeroFloat": 0.0, + "ZeroComplex": complex(0.0, 0.0), + "ZeroByteSlice": nil, + }, + fieldMask: []string{}, + allowZeroFields: []string{}, + }, + wantMasks: []string{}, + wantNulls: []string{}, + }, + { + name: "empty-field-mask-allow-all-zero-fields", + args: args{ + fieldValues: map[string]interface{}{ + "Boolean": true, + "Int": 100, + "String": "hello", + "Float": 1.1, + "Complex": complex(1.1, 1.1), + "ByteSlice": []byte("byte slice"), + "ZeroBoolean": false, + "ZeroInt": 0, + "ZeroString": "", + "ZeroFloat": 0.0, + "ZeroComplex": complex(0.0, 0.0), + "ZeroByteSlice": nil, + }, + fieldMask: []string{}, + allowZeroFields: []string{ + "Boolean", "Int", "String", "Float", "Complex", "ByteSlice", + "ZeroBoolean", "ZeroInt", "ZeroString", "ZeroFloat", "ZeroComplex", "ZeroByteSlice", + }, + }, + wantMasks: []string{}, + wantNulls: []string{}, + }, + { + name: "zero-fields-are-nulls", + args: args{ + fieldValues: map[string]interface{}{ + "Boolean": true, + "Int": 100, + "String": "hello", + "Float": 1.1, + "Complex": complex(1.1, 1.1), + "ByteSlice": []byte("byte slice"), + "ZeroBoolean": false, + "ZeroInt": 0, + "ZeroString": "", + "ZeroFloat": 0.0, + "ZeroComplex": complex(0.0, 0.0), + "ZeroByteSlice": nil, + }, + fieldMask: []string{ + "Boolean", "Int", "String", "Float", "Complex", "ByteSlice", + "ZeroBoolean", "ZeroInt", "ZeroString", "ZeroFloat", "ZeroComplex", "ZeroByteSlice", + }, + allowZeroFields: []string{}, + }, + wantMasks: []string{ + "Boolean", "Int", "String", "Float", "Complex", "ByteSlice", + }, + wantNulls: []string{ + "ZeroBoolean", "ZeroInt", "ZeroString", "ZeroFloat", "ZeroComplex", "ZeroByteSlice", + }, + }, + { + name: "all-zero-fields-allowed-no-nulls", + args: args{ + fieldValues: map[string]interface{}{ + "Boolean": true, + "Int": 100, + "String": "hello", + "Float": 1.1, + "Complex": complex(1.1, 1.1), + "ByteSlice": []byte("byte slice"), + "ZeroBoolean": false, + "ZeroInt": 0, + "ZeroString": "", + "ZeroFloat": 0.0, + "ZeroComplex": complex(0.0, 0.0), + "ZeroByteSlice": nil, + }, + fieldMask: []string{ + "Boolean", "Int", "String", "Float", "Complex", "ByteSlice", + "ZeroBoolean", "ZeroInt", "ZeroString", "ZeroFloat", "ZeroComplex", "ZeroByteSlice", + }, + allowZeroFields: []string{ + "ZeroBoolean", "ZeroInt", "ZeroString", "ZeroFloat", "ZeroComplex", "ZeroByteSlice", + }, + }, + wantMasks: []string{ + "Boolean", "Int", "String", "Float", "Complex", "ByteSlice", + "ZeroBoolean", "ZeroInt", "ZeroString", "ZeroFloat", "ZeroComplex", "ZeroByteSlice", + }, + wantNulls: []string{}, + }, + { + name: "non-zeros-allowed-as-zero-fields", + args: args{ + fieldValues: map[string]interface{}{ + "Boolean": true, + "Int": 100, + "String": "hello", + "Float": 1.1, + "Complex": complex(1.1, 1.1), + "ByteSlice": []byte("byte slice"), + "ZeroBoolean": false, + "ZeroInt": 0, + "ZeroString": "", + "ZeroFloat": 0.0, + "ZeroComplex": complex(0.0, 0.0), + "ZeroByteSlice": nil, + }, + fieldMask: []string{ + "Boolean", "Int", "String", "Float", "Complex", "ByteSlice", + "ZeroBoolean", "ZeroInt", "ZeroString", "ZeroFloat", "ZeroComplex", "ZeroByteSlice", + }, + allowZeroFields: []string{ + "Boolean", "Int", "String", "Float", "Complex", "ByteSlice", + }, + }, + wantMasks: []string{ + "Boolean", "Int", "String", "Float", "Complex", "ByteSlice", + }, + wantNulls: []string{ + "ZeroBoolean", "ZeroInt", "ZeroString", "ZeroFloat", "ZeroComplex", "ZeroByteSlice", + }, + }, + { + name: "only-zero-fields-in-fieldmask", + args: args{ + fieldValues: map[string]interface{}{ + "Boolean": true, + "Int": 100, + "String": "hello", + "Float": 1.1, + "Complex": complex(1.1, 1.1), + "ByteSlice": []byte("byte slice"), + "ZeroBoolean": false, + "ZeroInt": 0, + "ZeroString": "", + "ZeroFloat": 0.0, + "ZeroComplex": complex(0.0, 0.0), + "ZeroByteSlice": nil, + }, + fieldMask: []string{ + "ZeroBoolean", "ZeroInt", "ZeroString", "ZeroFloat", "ZeroComplex", "ZeroByteSlice", + }, + allowZeroFields: []string{ + "Boolean", "Int", "String", "Float", "Complex", "ByteSlice", + }, + }, + wantMasks: []string{}, + wantNulls: []string{ + "ZeroBoolean", "ZeroInt", "ZeroString", "ZeroFloat", "ZeroComplex", "ZeroByteSlice", + }, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert := assert.New(t) + gotMasks, gotNulls := BuildUpdatePaths(tt.args.fieldValues, tt.args.fieldMask, tt.args.allowZeroFields) + assert.ElementsMatch(tt.wantMasks, gotMasks, "masks") + assert.ElementsMatch(tt.wantNulls, gotNulls, "nulls") + }) + } +}