blob: f8af5a5eb920bf92e293e3a1b5ea981b40363fb8 [file] [log] [blame]
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package random
import (
"encoding/json"
"reflect"
"testing"
)
func TestParsePolicy(t *testing.T) {
type testCase struct {
rawConfig string
expected StringGenerator
expectErr bool
}
tests := map[string]testCase{
"unrecognized rule": {
rawConfig: `
length = 20
rule "testrule" {
string = "teststring"
int = 123
}`,
expected: StringGenerator{},
expectErr: true,
},
"charset restrictions": {
rawConfig: `
length = 20
rule "charset" {
charset = "abcde"
min-chars = 2
}`,
expected: StringGenerator{
Length: 20,
charset: []rune("abcde"),
Rules: []Rule{
CharsetRule{
Charset: []rune("abcde"),
MinChars: 2,
},
},
},
expectErr: false,
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
actual, err := ParsePolicy(test.rawConfig)
if test.expectErr && err == nil {
t.Fatalf("err expected, got nil")
}
if !test.expectErr && err != nil {
t.Fatalf("no error expected, got: %s", err)
}
if !reflect.DeepEqual(actual, test.expected) {
t.Fatalf("Actual: %#v\nExpected:%#v", actual, test.expected)
}
})
}
}
func TestParser_ParsePolicy(t *testing.T) {
type testCase struct {
registry map[string]ruleConstructor
rawConfig string
expected StringGenerator
expectErr bool
}
tests := map[string]testCase{
"empty config": {
registry: defaultRuleNameMapping,
rawConfig: "",
expected: StringGenerator{},
expectErr: true,
},
"bogus config": {
registry: defaultRuleNameMapping,
rawConfig: "asdf",
expected: StringGenerator{},
expectErr: true,
},
"config with only length": {
registry: defaultRuleNameMapping,
rawConfig: `
length = 20`,
expected: StringGenerator{},
expectErr: true,
},
"config with zero length": {
registry: defaultRuleNameMapping,
rawConfig: `
length = 0
rule "charset" {
charset = "abcde"
}`,
expected: StringGenerator{},
expectErr: true,
},
"config with negative length": {
registry: defaultRuleNameMapping,
rawConfig: `
length = -2
rule "charset" {
charset = "abcde"
}`,
expected: StringGenerator{},
expectErr: true,
},
"charset restrictions": {
registry: defaultRuleNameMapping,
rawConfig: `
length = 20
rule "charset" {
charset = "abcde"
min-chars = 2
}`,
expected: StringGenerator{
Length: 20,
charset: []rune("abcde"),
Rules: []Rule{
CharsetRule{
Charset: []rune("abcde"),
MinChars: 2,
},
},
},
expectErr: false,
},
"test rule": {
registry: map[string]ruleConstructor{
"testrule": newTestRule,
},
rawConfig: `
length = 20
rule "testrule" {
string = "teststring"
int = 123
}`,
expected: StringGenerator{
Length: 20,
charset: deduplicateRunes([]rune("teststring")),
Rules: []Rule{
testCharsetRule{
String: "teststring",
Integer: 123,
},
},
},
expectErr: false,
},
"test rule and charset restrictions": {
registry: map[string]ruleConstructor{
"testrule": newTestRule,
"charset": ParseCharset,
},
rawConfig: `
length = 20
rule "testrule" {
string = "teststring"
int = 123
}
rule "charset" {
charset = "abcde"
min-chars = 2
}`,
expected: StringGenerator{
Length: 20,
charset: deduplicateRunes([]rune("abcdeteststring")),
Rules: []Rule{
testCharsetRule{
String: "teststring",
Integer: 123,
},
CharsetRule{
Charset: []rune("abcde"),
MinChars: 2,
},
},
},
expectErr: false,
},
"unrecognized rule": {
registry: defaultRuleNameMapping,
rawConfig: `
length = 20
rule "testrule" {
string = "teststring"
int = 123
}`,
expected: StringGenerator{},
expectErr: true,
},
// /////////////////////////////////////////////////
// JSON data
"manually JSONified HCL": {
registry: map[string]ruleConstructor{
"testrule": newTestRule,
"charset": ParseCharset,
},
rawConfig: `
{
"charset": "abcde",
"length": 20,
"rule": [
{
"testrule": [
{
"string": "teststring",
"int": 123
}
]
},
{
"charset": [
{
"charset": "abcde",
"min-chars": 2
}
]
}
]
}`,
expected: StringGenerator{
Length: 20,
charset: deduplicateRunes([]rune("abcdeteststring")),
Rules: []Rule{
testCharsetRule{
String: "teststring",
Integer: 123,
},
CharsetRule{
Charset: []rune("abcde"),
MinChars: 2,
},
},
},
expectErr: false,
},
"JSONified HCL": {
registry: map[string]ruleConstructor{
"testrule": newTestRule,
"charset": ParseCharset,
},
rawConfig: toJSON(t, StringGenerator{
Length: 20,
Rules: []Rule{
testCharsetRule{
String: "teststring",
Integer: 123,
},
CharsetRule{
Charset: []rune("abcde"),
MinChars: 2,
},
},
}),
expected: StringGenerator{
Length: 20,
charset: deduplicateRunes([]rune("abcdeteststring")),
Rules: []Rule{
testCharsetRule{
String: "teststring",
Integer: 123,
},
CharsetRule{
Charset: []rune("abcde"),
MinChars: 2,
},
},
},
expectErr: false,
},
"JSON unrecognized rule": {
registry: defaultRuleNameMapping,
rawConfig: `
{
"charset": "abcde",
"length": 20,
"rule": [
{
"testrule": [
{
"string": "teststring",
"int": 123
}
],
}
]
}`,
expected: StringGenerator{},
expectErr: true,
},
"config value with empty slice": {
registry: defaultRuleNameMapping,
rawConfig: `
rule {
n = []
}`,
expected: StringGenerator{},
expectErr: true,
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
parser := PolicyParser{
RuleRegistry: Registry{
Rules: test.registry,
},
}
actual, err := parser.ParsePolicy(test.rawConfig)
if test.expectErr && err == nil {
t.Fatalf("err expected, got nil")
}
if !test.expectErr && err != nil {
t.Fatalf("no error expected, got: %s", err)
}
if !reflect.DeepEqual(actual, test.expected) {
t.Fatalf("Actual: %#v\nExpected:%#v", actual, test.expected)
}
})
}
}
func TestParseRules(t *testing.T) {
type testCase struct {
registry map[string]ruleConstructor
rawRules []map[string]interface{}
expectedRules []Rule
expectErr bool
}
tests := map[string]testCase{
"nil rule data": {
registry: defaultRuleNameMapping,
rawRules: nil,
expectedRules: nil,
expectErr: false,
},
"empty rule data": {
registry: defaultRuleNameMapping,
rawRules: []map[string]interface{}{},
expectedRules: nil,
expectErr: false,
},
"invalid rule data": {
registry: defaultRuleNameMapping,
rawRules: []map[string]interface{}{
{
"testrule": map[string]interface{}{
"string": "teststring",
},
},
},
expectedRules: nil,
expectErr: true,
},
"unrecognized rule data": {
registry: defaultRuleNameMapping,
rawRules: []map[string]interface{}{
{
"testrule": []map[string]interface{}{
{
"string": "teststring",
"int": 123,
},
},
},
},
expectedRules: nil,
expectErr: true,
},
"recognized rule": {
registry: map[string]ruleConstructor{
"testrule": newTestRule,
},
rawRules: []map[string]interface{}{
{
"testrule": []map[string]interface{}{
{
"string": "teststring",
"int": 123,
},
},
},
},
expectedRules: []Rule{
testCharsetRule{
String: "teststring",
Integer: 123,
},
},
expectErr: false,
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
registry := Registry{
Rules: test.registry,
}
actualRules, err := parseRules(registry, test.rawRules)
if test.expectErr && err == nil {
t.Fatalf("err expected, got nil")
}
if !test.expectErr && err != nil {
t.Fatalf("no error expected, got: %s", err)
}
if !reflect.DeepEqual(actualRules, test.expectedRules) {
t.Fatalf("Actual: %#v\nExpected:%#v", actualRules, test.expectedRules)
}
})
}
}
func TestGetMapSlice(t *testing.T) {
type testCase struct {
input map[string]interface{}
key string
expectedSlice []map[string]interface{}
expectErr bool
}
tests := map[string]testCase{
"nil map": {
input: nil,
key: "testkey",
expectedSlice: nil,
expectErr: false,
},
"empty map": {
input: map[string]interface{}{},
key: "testkey",
expectedSlice: nil,
expectErr: false,
},
"ignored keys": {
input: map[string]interface{}{
"foo": "bar",
},
key: "testkey",
expectedSlice: nil,
expectErr: false,
},
"key has wrong type": {
input: map[string]interface{}{
"foo": "bar",
},
key: "foo",
expectedSlice: nil,
expectErr: true,
},
"good data": {
input: map[string]interface{}{
"foo": []map[string]interface{}{
{
"sub-foo": "bar",
},
},
},
key: "foo",
expectedSlice: []map[string]interface{}{
{
"sub-foo": "bar",
},
},
expectErr: false,
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
actualSlice, err := getMapSlice(test.input, test.key)
if test.expectErr && err == nil {
t.Fatalf("err expected, got nil")
}
if !test.expectErr && err != nil {
t.Fatalf("no error expected, got: %s", err)
}
if !reflect.DeepEqual(actualSlice, test.expectedSlice) {
t.Fatalf("Actual: %#v\nExpected:%#v", actualSlice, test.expectedSlice)
}
})
}
}
func TestGetRuleInfo(t *testing.T) {
type testCase struct {
rule map[string]interface{}
expectedInfo ruleInfo
expectErr bool
}
tests := map[string]testCase{
"nil rule": {
rule: nil,
expectedInfo: ruleInfo{},
expectErr: true,
},
"empty rule": {
rule: map[string]interface{}{},
expectedInfo: ruleInfo{},
expectErr: true,
},
"rule with invalid type": {
rule: map[string]interface{}{
"TestRuleType": "wrong type",
},
expectedInfo: ruleInfo{},
expectErr: true,
},
"rule with good data": {
rule: map[string]interface{}{
"TestRuleType": []map[string]interface{}{
{
"foo": "bar",
},
},
},
expectedInfo: ruleInfo{
ruleType: "TestRuleType",
data: map[string]interface{}{
"foo": "bar",
},
},
expectErr: false,
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
actualInfo, err := getRuleInfo(test.rule)
if test.expectErr && err == nil {
t.Fatalf("err expected, got nil")
}
if !test.expectErr && err != nil {
t.Fatalf("no error expected, got: %s", err)
}
if !reflect.DeepEqual(actualInfo, test.expectedInfo) {
t.Fatalf("Actual: %#v\nExpected:%#v", actualInfo, test.expectedInfo)
}
})
}
}
func BenchmarkParser_Parse(b *testing.B) {
config := `length = 20
rule "charset" {
charset = "abcde"
min-chars = 2
}`
for i := 0; i < b.N; i++ {
parser := PolicyParser{
RuleRegistry: Registry{
Rules: defaultRuleNameMapping,
},
}
_, err := parser.ParsePolicy(config)
if err != nil {
b.Fatalf("Failed to parse: %s", err)
}
}
}
func toJSON(t *testing.T, val interface{}) string {
t.Helper()
b, err := json.Marshal(val)
if err != nil {
t.Fatalf("unable to marshal to JSON: %s", err)
}
return string(b)
}