1package assertions
2
3import (
4	"fmt"
5	"reflect"
6	"strings"
7)
8
9// ShouldStartWith receives exactly 2 string parameters and ensures that the first starts with the second.
10func ShouldStartWith(actual interface{}, expected ...interface{}) string {
11	if fail := need(1, expected); fail != success {
12		return fail
13	}
14
15	value, valueIsString := actual.(string)
16	prefix, prefixIsString := expected[0].(string)
17
18	if !valueIsString || !prefixIsString {
19		return fmt.Sprintf(shouldBothBeStrings, reflect.TypeOf(actual), reflect.TypeOf(expected[0]))
20	}
21
22	return shouldStartWith(value, prefix)
23}
24func shouldStartWith(value, prefix string) string {
25	if !strings.HasPrefix(value, prefix) {
26		shortval := value
27		if len(shortval) > len(prefix) {
28			shortval = shortval[:len(prefix)] + "..."
29		}
30		return serializer.serialize(prefix, shortval, fmt.Sprintf(shouldHaveStartedWith, value, prefix))
31	}
32	return success
33}
34
35// ShouldNotStartWith receives exactly 2 string parameters and ensures that the first does not start with the second.
36func ShouldNotStartWith(actual interface{}, expected ...interface{}) string {
37	if fail := need(1, expected); fail != success {
38		return fail
39	}
40
41	value, valueIsString := actual.(string)
42	prefix, prefixIsString := expected[0].(string)
43
44	if !valueIsString || !prefixIsString {
45		return fmt.Sprintf(shouldBothBeStrings, reflect.TypeOf(actual), reflect.TypeOf(expected[0]))
46	}
47
48	return shouldNotStartWith(value, prefix)
49}
50func shouldNotStartWith(value, prefix string) string {
51	if strings.HasPrefix(value, prefix) {
52		if value == "" {
53			value = "<empty>"
54		}
55		if prefix == "" {
56			prefix = "<empty>"
57		}
58		return fmt.Sprintf(shouldNotHaveStartedWith, value, prefix)
59	}
60	return success
61}
62
63// ShouldEndWith receives exactly 2 string parameters and ensures that the first ends with the second.
64func ShouldEndWith(actual interface{}, expected ...interface{}) string {
65	if fail := need(1, expected); fail != success {
66		return fail
67	}
68
69	value, valueIsString := actual.(string)
70	suffix, suffixIsString := expected[0].(string)
71
72	if !valueIsString || !suffixIsString {
73		return fmt.Sprintf(shouldBothBeStrings, reflect.TypeOf(actual), reflect.TypeOf(expected[0]))
74	}
75
76	return shouldEndWith(value, suffix)
77}
78func shouldEndWith(value, suffix string) string {
79	if !strings.HasSuffix(value, suffix) {
80		shortval := value
81		if len(shortval) > len(suffix) {
82			shortval = "..." + shortval[len(shortval)-len(suffix):]
83		}
84		return serializer.serialize(suffix, shortval, fmt.Sprintf(shouldHaveEndedWith, value, suffix))
85	}
86	return success
87}
88
89// ShouldEndWith receives exactly 2 string parameters and ensures that the first does not end with the second.
90func ShouldNotEndWith(actual interface{}, expected ...interface{}) string {
91	if fail := need(1, expected); fail != success {
92		return fail
93	}
94
95	value, valueIsString := actual.(string)
96	suffix, suffixIsString := expected[0].(string)
97
98	if !valueIsString || !suffixIsString {
99		return fmt.Sprintf(shouldBothBeStrings, reflect.TypeOf(actual), reflect.TypeOf(expected[0]))
100	}
101
102	return shouldNotEndWith(value, suffix)
103}
104func shouldNotEndWith(value, suffix string) string {
105	if strings.HasSuffix(value, suffix) {
106		if value == "" {
107			value = "<empty>"
108		}
109		if suffix == "" {
110			suffix = "<empty>"
111		}
112		return fmt.Sprintf(shouldNotHaveEndedWith, value, suffix)
113	}
114	return success
115}
116
117// ShouldContainSubstring receives exactly 2 string parameters and ensures that the first contains the second as a substring.
118func ShouldContainSubstring(actual interface{}, expected ...interface{}) string {
119	if fail := need(1, expected); fail != success {
120		return fail
121	}
122
123	long, longOk := actual.(string)
124	short, shortOk := expected[0].(string)
125
126	if !longOk || !shortOk {
127		return fmt.Sprintf(shouldBothBeStrings, reflect.TypeOf(actual), reflect.TypeOf(expected[0]))
128	}
129
130	if !strings.Contains(long, short) {
131		return serializer.serialize(expected[0], actual, fmt.Sprintf(shouldHaveContainedSubstring, long, short))
132	}
133	return success
134}
135
136// ShouldNotContainSubstring receives exactly 2 string parameters and ensures that the first does NOT contain the second as a substring.
137func ShouldNotContainSubstring(actual interface{}, expected ...interface{}) string {
138	if fail := need(1, expected); fail != success {
139		return fail
140	}
141
142	long, longOk := actual.(string)
143	short, shortOk := expected[0].(string)
144
145	if !longOk || !shortOk {
146		return fmt.Sprintf(shouldBothBeStrings, reflect.TypeOf(actual), reflect.TypeOf(expected[0]))
147	}
148
149	if strings.Contains(long, short) {
150		return fmt.Sprintf(shouldNotHaveContainedSubstring, long, short)
151	}
152	return success
153}
154
155// ShouldBeBlank receives exactly 1 string parameter and ensures that it is equal to "".
156func ShouldBeBlank(actual interface{}, expected ...interface{}) string {
157	if fail := need(0, expected); fail != success {
158		return fail
159	}
160	value, ok := actual.(string)
161	if !ok {
162		return fmt.Sprintf(shouldBeString, reflect.TypeOf(actual))
163	}
164	if value != "" {
165		return serializer.serialize("", value, fmt.Sprintf(shouldHaveBeenBlank, value))
166	}
167	return success
168}
169
170// ShouldNotBeBlank receives exactly 1 string parameter and ensures that it is equal to "".
171func ShouldNotBeBlank(actual interface{}, expected ...interface{}) string {
172	if fail := need(0, expected); fail != success {
173		return fail
174	}
175	value, ok := actual.(string)
176	if !ok {
177		return fmt.Sprintf(shouldBeString, reflect.TypeOf(actual))
178	}
179	if value == "" {
180		return shouldNotHaveBeenBlank
181	}
182	return success
183}
184
185// ShouldEqualWithout receives exactly 3 string parameters and ensures that the first is equal to the second
186// after removing all instances of the third from the first using strings.Replace(first, third, "", -1).
187func ShouldEqualWithout(actual interface{}, expected ...interface{}) string {
188	if fail := need(2, expected); fail != success {
189		return fail
190	}
191	actualString, ok1 := actual.(string)
192	expectedString, ok2 := expected[0].(string)
193	replace, ok3 := expected[1].(string)
194
195	if !ok1 || !ok2 || !ok3 {
196		return fmt.Sprintf(shouldAllBeStrings, []reflect.Type{
197			reflect.TypeOf(actual),
198			reflect.TypeOf(expected[0]),
199			reflect.TypeOf(expected[1]),
200		})
201	}
202
203	replaced := strings.Replace(actualString, replace, "", -1)
204	if replaced == expectedString {
205		return ""
206	}
207
208	return fmt.Sprintf("Expected '%s' to equal '%s' but without any '%s' (but it didn't).", actualString, expectedString, replace)
209}
210
211// ShouldEqualTrimSpace receives exactly 2 string parameters and ensures that the first is equal to the second
212// after removing all leading and trailing whitespace using strings.TrimSpace(first).
213func ShouldEqualTrimSpace(actual interface{}, expected ...interface{}) string {
214	if fail := need(1, expected); fail != success {
215		return fail
216	}
217
218	actualString, valueIsString := actual.(string)
219	_, value2IsString := expected[0].(string)
220
221	if !valueIsString || !value2IsString {
222		return fmt.Sprintf(shouldBothBeStrings, reflect.TypeOf(actual), reflect.TypeOf(expected[0]))
223	}
224
225	actualString = strings.TrimSpace(actualString)
226	return ShouldEqual(actualString, expected[0])
227}
228