1package plugin
2
3import (
4	"context"
5	"fmt"
6	"regexp"
7	"testing"
8)
9
10func TestGeneratePassword(t *testing.T) {
11	type testCase struct {
12		passConf  passwordConf
13		generator passwordGenerator
14
15		passwordAssertion func(t *testing.T, password string)
16		expectErr         bool
17	}
18
19	tests := map[string]testCase{
20		"missing configs": {
21			passConf: passwordConf{
22				Length:         0,
23				Formatter:      "",
24				PasswordPolicy: "",
25			},
26			generator: nil,
27
28			passwordAssertion: assertNoPassword,
29			expectErr:         true,
30		},
31		"policy failure": {
32			passConf: passwordConf{
33				PasswordPolicy: "testpolicy",
34			},
35			generator:         makePasswordGenerator("", fmt.Errorf("test error")),
36			passwordAssertion: assertNoPassword,
37			expectErr:         true,
38		},
39		"successful policy": {
40			passConf: passwordConf{
41				PasswordPolicy: "testpolicy",
42			},
43			generator:         makePasswordGenerator("testpassword", nil),
44			passwordAssertion: assertPassword("testpassword"),
45			expectErr:         false,
46		},
47		"deprecated with no formatter": {
48			passConf: passwordConf{
49				Length: 50,
50			},
51			passwordAssertion: assertPasswordRegex(
52				fmt.Sprintf("^%s[a-zA-Z0-9]{%d}$",
53					regexp.QuoteMeta(passwordComplexityPrefix),
54					50-len(passwordComplexityPrefix),
55				),
56			),
57			expectErr: false,
58		},
59		"deprecated with formatter prefix": {
60			passConf: passwordConf{
61				Length:    50,
62				Formatter: "foobar{{PASSWORD}}",
63			},
64			passwordAssertion: assertPasswordRegex("^foobar[a-zA-Z0-9]{44}$"),
65			expectErr:         false,
66		},
67		"deprecated with formatter suffix": {
68			passConf: passwordConf{
69				Length:    50,
70				Formatter: "{{PASSWORD}}foobar",
71			},
72			passwordAssertion: assertPasswordRegex("^[a-zA-Z0-9]{44}foobar$"),
73			expectErr:         false,
74		},
75		"deprecated with formatter prefix and suffix": {
76			passConf: passwordConf{
77				Length:    50,
78				Formatter: "foo{{PASSWORD}}bar",
79			},
80			passwordAssertion: assertPasswordRegex("^foo[a-zA-Z0-9]{44}bar$"),
81			expectErr:         false,
82		},
83	}
84
85	for name, test := range tests {
86		t.Run(name, func(t *testing.T) {
87			password, err := GeneratePassword(context.Background(), test.passConf, test.generator)
88			if test.expectErr && err == nil {
89				t.Fatalf("err expected, got nil")
90			}
91			if !test.expectErr && err != nil {
92				t.Fatalf("no error expected, got: %s", err)
93			}
94			test.passwordAssertion(t, password)
95		})
96	}
97}
98
99func assertNoPassword(t *testing.T, password string) {
100	t.Helper()
101	if password != "" {
102		t.Fatalf("password should be empty")
103	}
104}
105
106func assertPassword(expectedPassword string) func(*testing.T, string) {
107	return func(t *testing.T, password string) {
108		t.Helper()
109		if password != expectedPassword {
110			t.Fatalf("Expected password %q but was %q", expectedPassword, password)
111		}
112	}
113}
114
115func assertPasswordRegex(rawRegex string) func(*testing.T, string) {
116	re := regexp.MustCompile(rawRegex)
117	return func(t *testing.T, password string) {
118		t.Helper()
119		if !re.MatchString(password) {
120			t.Fatalf("Password %q does not match regexp %q", password, rawRegex)
121		}
122	}
123}
124
125type fakeGenerator struct {
126	password string
127	err      error
128}
129
130func (g fakeGenerator) GeneratePasswordFromPolicy(_ context.Context, _ string) (password string, err error) {
131	return g.password, g.err
132}
133
134func makePasswordGenerator(returnedPass string, returnedErr error) passwordGenerator {
135	return fakeGenerator{
136		password: returnedPass,
137		err:      returnedErr,
138	}
139}
140