Repository: hashicorp/nomad
File: lib/auth/claims_test.go (Go)

Code before:
			Name: "slice of interface{}",
			Input: []interface{}{
				"foo", 42, true,
			},
			Expected: `["foo",42,true]`,
			OK:       true,
		},
		{
			Name:     "unknown type",
			Input:    struct{ X int }{X: 1},
			Expected: "",
			OK:       false,
		},
		{
			Name: "invalid JSON slice element",
			Input: []interface{}{
				func() {},
			},
			Expected: "",
			OK:       false,
		},
	}

	for _, tt := range cases {
		t.Run(tt.Name, func(t *testing.T) {
			out, ok := stringifyClaimValue(tt.Input)
			must.Eq(t, tt.OK, ok)
			must.Eq(t, tt.Expected, out)
		})
	}
}


Review comment (suggestion):
Thanks for adding a test as asked. But `stringifyClaimValue` isn't all that useful of a unit to test -- it's just a type switch + a json encoding. The test cases end up doing things like calling `strconv` for the expectation rather than providing the expected string directly.

Let's drop this test and instead add one test good test case of the JSON encoding code path to the `TestSelectorData` test. That way we're exercising the handling of the data all the way through. Something like this for the input:

```go
map[string]any{
	"roles": []any{
		[]any{"role1", "role2", "roleN"}, 42, false,
	},
}
```


Code after:
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

package auth

import (
	"testing"

	"github.com/shoenig/test/must"

	"github.com/hashicorp/nomad/nomad/structs"
)

func TestSelectorData(t *testing.T) {
	cases := []struct {
		Name        string
		Mapping     map[string]string
		ListMapping map[string]string
		Data        map[string]interface{}
		Expected    *structs.ACLAuthClaims
	}{
		{
			"no mappings",
			nil,
			nil,
			map[string]interface{}{"iss": "https://hashicorp.com"},
			&structs.ACLAuthClaims{
				Value: map[string]string{},
				List:  map[string][]string{},
			},
		},

		{
			"key",
			map[string]string{"iss": "issuer"},
			nil,
			map[string]interface{}{"iss": "https://hashicorp.com"},
			&structs.ACLAuthClaims{
				Value: map[string]string{"issuer": "https://hashicorp.com"},
				List:  map[string][]string{},
			},
		},

		{
			"key doesn't exist",
			map[string]string{"iss": "issuer"},
			nil,
			map[string]interface{}{"nope": "https://hashicorp.com"},
			&structs.ACLAuthClaims{
				Value: map[string]string{},
				List:  map[string][]string{},
			},
		},

		{
			"list",
			nil,
			map[string]string{"groups": "g"},
			map[string]interface{}{
				"groups": []interface{}{
					"A", 42, false,
				},
			},
			&structs.ACLAuthClaims{
				Value: map[string]string{},
				List: map[string][]string{
					"g": {"A", "42", "false"},
				},
			},
		},

		{
			"nested list claim",
			nil,
			map[string]string{"roles": "r"},
			map[string]any{
				"roles": []any{
					[]any{"role1", "role2", "roleN"}, 42, false,
				},
			},
			&structs.ACLAuthClaims{
				Value: map[string]string{},
				List: map[string][]string{
					"r": {`["role1","role2","roleN"]`, "42", "false"},
				},
			},
		},

	}

	for _, tt := range cases {
		t.Run(tt.Name, func(t *testing.T) {

			am := &structs.ACLAuthMethod{
				Config: &structs.ACLAuthMethodConfig{
					ClaimMappings:     tt.Mapping,
					ListClaimMappings: tt.ListMapping,
				},
			}

			// Get real selector data
			actual, err := SelectorData(am, tt.Data, nil)
			must.NoError(t, err)
			must.Eq(t, actual, tt.Expected)
		})
	}
}