Skip to content

Commit 257eece

Browse files
committed
[configuration] extend template test package, incl. utility and inventory functions
1 parent 6861011 commit 257eece

File tree

9 files changed

+419
-10
lines changed

9 files changed

+419
-10
lines changed

common/gera/map.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,10 @@ type Map[K comparable, V any] interface {
5151
Set(key K, value V) bool
5252
Del(key K) bool
5353

54+
// Flattened should return a flat representation of the tree in the root direction,
55+
// where children kv pairs overwrite parent kv pairs.
5456
Flattened() (map[K]V, error)
57+
// FlattenedParent should return a flat representation of the tree in the root direction,
5558
FlattenedParent() (map[K]V, error)
5659
WrappedAndFlattened(m Map[K, V]) (map[K]V, error)
5760

configuration/template/dplutil.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func extractConfigURIs(dplCommand string) (uris []string) {
4747
// Match any consul/apricot URI
4848
// it would be the easiest to use a backreference in the regex, but regexp does not support those:
4949
// (['"]?)((consul-json|apricot)://[^ |\n]*)(\1)
50-
re := regexp.MustCompile(`['"]?(consul-json|apricot)://[^ |\n]*`)
50+
re := regexp.MustCompile(`['"]?(consul-json|apricot)://[^ |\n]+`)
5151
matches := re.FindAllStringSubmatch(dplCommand, nMaxExpectedQcPayloads)
5252

5353
for _, match := range matches {

configuration/template/dplutil_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ var _ = Describe("DPL utilities", func() {
1616
Expect(uris).To(HaveLen(0))
1717
})
1818
})
19+
When("URI is not complete", func() {
20+
BeforeEach(func() {
21+
uris = extractConfigURIs("myexe --config apricot://")
22+
})
23+
It("should return an empty slice", func() {
24+
Expect(uris).To(HaveLen(0))
25+
})
26+
})
1927
When("the URI is the last argument", func() {
2028
BeforeEach(func() {
2129
uris = extractConfigURIs("myexe --config apricot://host.cern.ch:12345/components/qc/ANY/any/ctp-raw")

configuration/template/fieldwrappers.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,15 @@ func WrapGeneric(getterF GetterFunc, setterF SetterFunc) Field {
6767
}
6868
}
6969

70+
// WrapMapItems creates a slice of Fields from a map of string key-value pairs.
71+
// It wraps each map item in a GenericWrapper, allowing for dynamic access and
72+
// modification of the original map's values through the Fields interface.
73+
//
74+
// Parameters:
75+
// - items: A map[string]string to be wrapped
76+
//
77+
// Returns:
78+
// - Fields: A slice of Field interfaces, each corresponding to a map item
7079
func WrapMapItems(items map[string]string) Fields {
7180
fields := make(Fields, 0)
7281
for k := range items {
@@ -83,6 +92,16 @@ func WrapMapItems(items map[string]string) Fields {
8392
return fields
8493
}
8594

95+
// WrapSliceItems creates a slice of Fields from a slice of strings.
96+
// It wraps each string item in a GenericWrapper, allowing for dynamic
97+
// access and modification of the original slice's elements through
98+
// the Fields interface.
99+
//
100+
// Parameters:
101+
// - items: A []string to be wrapped
102+
//
103+
// Returns:
104+
// - Fields: A slice of Field interfaces, each corresponding to a slice item
86105
func WrapSliceItems(items []string) Fields {
87106
fields := make(Fields, 0)
88107
for i := range items {
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package template_test
2+
3+
import (
4+
"github.com/AliceO2Group/Control/configuration/template"
5+
. "github.com/onsi/ginkgo/v2"
6+
. "github.com/onsi/gomega"
7+
)
8+
9+
var _ = Describe("Field Wrappers", func() {
10+
Describe("PointerWrapper", func() {
11+
It("should get and set values correctly", func() {
12+
value := "test"
13+
pw := template.WrapPointer(&value)
14+
15+
Expect(pw.Get()).To(Equal("test"))
16+
17+
pw.Set("new value")
18+
Expect(value).To(Equal("new value"))
19+
})
20+
})
21+
22+
Describe("GenericWrapper", func() {
23+
It("should get and set values correctly", func() {
24+
value := "test"
25+
gw := template.WrapGeneric(
26+
func() string { return value },
27+
func(v string) { value = v },
28+
)
29+
30+
Expect(gw.Get()).To(Equal("test"))
31+
32+
gw.Set("new value")
33+
Expect(value).To(Equal("new value"))
34+
})
35+
})
36+
37+
Describe("WrapMapItems", func() {
38+
It("should wrap map items correctly", func() {
39+
items := map[string]string{
40+
"key1": "value1",
41+
"key2": "value2",
42+
}
43+
fields := template.WrapMapItems(items)
44+
45+
Expect(fields).To(HaveLen(2))
46+
47+
for _, field := range fields {
48+
initialValue := field.Get()
49+
field.Set("new " + initialValue)
50+
}
51+
52+
expectedItems := map[string]string{
53+
"key1": "new value1",
54+
"key2": "new value2",
55+
}
56+
57+
Expect(items).To(Equal(expectedItems))
58+
})
59+
})
60+
61+
Describe("WrapSliceItems", func() {
62+
It("should wrap slice items correctly", func() {
63+
items := []string{"item1", "item2", "item3"}
64+
fields := template.WrapSliceItems(items)
65+
66+
Expect(fields).To(HaveLen(3))
67+
68+
for i, field := range fields {
69+
Expect(field.Get()).To(Equal(items[i]))
70+
field.Set("new " + items[i])
71+
}
72+
73+
expectedItems := []string{"new item1", "new item2", "new item3"}
74+
75+
Expect(items).To(Equal(expectedItems))
76+
})
77+
})
78+
})
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
package template_test
2+
3+
import (
4+
"github.com/AliceO2Group/Control/apricot/local"
5+
. "github.com/onsi/ginkgo/v2"
6+
. "github.com/onsi/gomega"
7+
8+
"github.com/AliceO2Group/Control/configuration/template"
9+
)
10+
11+
var _ = Describe("Template function stack", func() {
12+
13+
Describe("utility functions", func() {
14+
var utilFuncMap map[string]interface{}
15+
16+
BeforeEach(func() {
17+
varStack := map[string]string{
18+
"test_var": "test_value",
19+
"prefix_var": "prefixed_value",
20+
"fallback_var": "fallback_value",
21+
"prefixed_none_fallback_var": "none",
22+
}
23+
utilFuncMap = template.MakeUtilFuncMap(varStack)
24+
})
25+
26+
Context("strings functions", func() {
27+
It("should validate inputs and convert string to int", func() {
28+
atoiFunc := utilFuncMap["Atoi"].(func(string) int)
29+
30+
result := atoiFunc("123")
31+
Expect(result).To(Equal(123))
32+
// we only produce an error log if unexpected input appears
33+
result = atoiFunc("abc")
34+
Expect(result).To(Equal(0))
35+
result = atoiFunc("")
36+
Expect(result).To(Equal(0))
37+
})
38+
39+
It("should validate inputs and convert int to string", func() {
40+
itoaFunc := utilFuncMap["Itoa"].(func(int) string)
41+
42+
Expect(itoaFunc(123)).To(Equal("123"))
43+
Expect(itoaFunc(0)).To(Equal("0"))
44+
Expect(itoaFunc(-456)).To(Equal("-456"))
45+
})
46+
47+
It("should trim quotes", func() {
48+
trimQuotesFunc := utilFuncMap["TrimQuotes"].(func(string) string)
49+
50+
Expect(trimQuotesFunc("\"test\"")).To(Equal("test"))
51+
// Fixme: should it support also single quotes?
52+
//Expect(trimQuotesFunc("'test'")).To(Equal("test"))
53+
54+
Expect(trimQuotesFunc("test")).To(Equal("test"))
55+
Expect(trimQuotesFunc("")).To(Equal(""))
56+
Expect(trimQuotesFunc("\"test")).To(Equal("test"))
57+
Expect(trimQuotesFunc("test\"")).To(Equal("test"))
58+
})
59+
60+
It("should trim spaces", func() {
61+
trimSpaceFunc := utilFuncMap["TrimSpace"].(func(string) string)
62+
63+
Expect(trimSpaceFunc(" test ")).To(Equal("test"))
64+
Expect(trimSpaceFunc("test")).To(Equal("test"))
65+
Expect(trimSpaceFunc("test test ")).To(Equal("test test"))
66+
})
67+
68+
It("should convert to upper case", func() {
69+
toUpperFunc := utilFuncMap["ToUpper"].(func(string) string)
70+
Expect(toUpperFunc("test")).To(Equal("TEST"))
71+
Expect(toUpperFunc("Test")).To(Equal("TEST"))
72+
Expect(toUpperFunc("1")).To(Equal("1"))
73+
})
74+
75+
It("should convert to lower case", func() {
76+
toLowerFunc := utilFuncMap["ToLower"].(func(string) string)
77+
Expect(toLowerFunc("TEST")).To(Equal("test"))
78+
Expect(toLowerFunc("Test")).To(Equal("test"))
79+
Expect(toLowerFunc("1")).To(Equal("1"))
80+
})
81+
82+
It("should check if a string is truthy", func() {
83+
isTruthyFunc := utilFuncMap["IsTruthy"].(func(string) bool)
84+
85+
Expect(isTruthyFunc("true")).To(BeTrue())
86+
Expect(isTruthyFunc("TRUE")).To(BeTrue())
87+
Expect(isTruthyFunc("yes")).To(BeTrue())
88+
Expect(isTruthyFunc("y")).To(BeTrue())
89+
Expect(isTruthyFunc("1")).To(BeTrue())
90+
Expect(isTruthyFunc("on")).To(BeTrue())
91+
Expect(isTruthyFunc("ok")).To(BeTrue())
92+
93+
Expect(isTruthyFunc("false")).To(BeFalse())
94+
Expect(isTruthyFunc("")).To(BeFalse())
95+
Expect(isTruthyFunc("0")).To(BeFalse())
96+
Expect(isTruthyFunc("truthy")).To(BeFalse())
97+
})
98+
99+
It("should check if a string is falsy", func() {
100+
isFalsyFunc := utilFuncMap["IsFalsy"].(func(string) bool)
101+
Expect(isFalsyFunc("false")).To(BeTrue())
102+
Expect(isFalsyFunc("FALSE")).To(BeTrue())
103+
Expect(isFalsyFunc("no")).To(BeTrue())
104+
Expect(isFalsyFunc("n")).To(BeTrue())
105+
Expect(isFalsyFunc("0")).To(BeTrue())
106+
Expect(isFalsyFunc("off")).To(BeTrue())
107+
Expect(isFalsyFunc("none")).To(BeTrue())
108+
Expect(isFalsyFunc("")).To(BeTrue())
109+
110+
Expect(isFalsyFunc("true")).To(BeFalse())
111+
Expect(isFalsyFunc("1")).To(BeFalse())
112+
})
113+
})
114+
115+
Context("json functions", func() {
116+
It("should unmarshal JSON", func() {
117+
unmarshalFunc := utilFuncMap["json"].(map[string]interface{})["Unmarshal"].(func(string) interface{})
118+
result := unmarshalFunc(`{"key": "value"}`)
119+
Expect(result).To(HaveKeyWithValue("key", "value"))
120+
})
121+
122+
It("should marshal to JSON", func() {
123+
marshalFunc := utilFuncMap["json"].(map[string]interface{})["Marshal"].(func(interface{}) string)
124+
input := map[string]string{"key": "value"}
125+
result := marshalFunc(input)
126+
Expect(result).To(MatchJSON(`{"key": "value"}`))
127+
})
128+
})
129+
130+
Context("uid functions", func() {
131+
It("should generate a new UID", func() {
132+
newFunc := utilFuncMap["uid"].(map[string]interface{})["New"].(func() string)
133+
uid1 := newFunc()
134+
uid2 := newFunc()
135+
Expect(uid1).NotTo(Equal(uid2))
136+
})
137+
})
138+
139+
Context("util functions", func() {
140+
It("should handle prefixed override", func() {
141+
prefixedOverrideFunc := utilFuncMap["util"].(map[string]interface{})["PrefixedOverride"].(func(string, string) string)
142+
Expect(prefixedOverrideFunc("var", "prefix")).To(Equal("prefixed_value"))
143+
Expect(prefixedOverrideFunc("fallback_var", "nonexistent_prefix")).To(Equal("fallback_value"))
144+
Expect(prefixedOverrideFunc("fallback_var", "prefixed_none")).To(Equal("fallback_value"))
145+
Expect(prefixedOverrideFunc("nonexistent_fallback_var", "prefix")).To(Equal(""))
146+
})
147+
148+
It("should handle nullable strings", func() {
149+
nullableFunc := utilFuncMap["util"].(map[string]interface{})["Nullable"].(func(*string) string)
150+
var nilString *string
151+
Expect(nullableFunc(nilString)).To(Equal(""))
152+
153+
nonNilString := "test"
154+
Expect(nullableFunc(&nonNilString)).To(Equal("test"))
155+
})
156+
157+
It("should check if suffix is in range", func() {
158+
suffixInRangeFunc := utilFuncMap["util"].(map[string]interface{})["SuffixInRange"].(func(string, string, string, string) string)
159+
Expect(suffixInRangeFunc("test5", "test", "1", "10")).To(Equal("true"))
160+
Expect(suffixInRangeFunc("test15", "test", "1", "10")).To(Equal("false"))
161+
// no prefix
162+
Expect(suffixInRangeFunc("other5", "test", "1", "10")).To(Equal("false"))
163+
// no suffix
164+
Expect(suffixInRangeFunc("test", "test", "1", "10")).To(Equal("false"))
165+
// range arguments are not numbers
166+
Expect(suffixInRangeFunc("test", "test", "a", "10")).To(Equal("false"))
167+
Expect(suffixInRangeFunc("test", "test", "1", "b")).To(Equal("false"))
168+
})
169+
})
170+
})
171+
172+
Describe("inventory functions", func() {
173+
var (
174+
svc *local.Service
175+
configAccessObject map[string]interface{}
176+
err error
177+
)
178+
BeforeEach(func() {
179+
svc, err = local.NewService("file://" + *tmpDir + "/" + serviceConfigFile)
180+
Expect(err).NotTo(HaveOccurred())
181+
182+
varStack := map[string]string{}
183+
configAccessObject = template.MakeConfigAccessObject(svc, varStack)
184+
})
185+
186+
It("should get detector for host", func() {
187+
getDetectorFunc := configAccessObject["inventory"].(map[string]interface{})["DetectorForHost"].(func(string) string)
188+
Expect(getDetectorFunc("flp001")).To(Equal("ABC"))
189+
Expect(getDetectorFunc("NOPE")).To(ContainSubstring("error"))
190+
})
191+
It("should get detectors for a list of hosts", func() {
192+
getDetectorsFunc := configAccessObject["inventory"].(map[string]interface{})["DetectorsForHosts"].(func(string) string)
193+
Expect(getDetectorsFunc("[ \"flp001\", \"flp002\" ]")).To(Equal("[\"ABC\",\"DEF\"]"))
194+
Expect(getDetectorsFunc("[ \"flp001\", \"NOPE\" ]")).To(ContainSubstring("error"))
195+
Expect(getDetectorsFunc("[ \"NOPE\" ]")).To(ContainSubstring("error"))
196+
Expect(getDetectorsFunc("flp001")).To(ContainSubstring("error"))
197+
})
198+
It("should get CRU cards for host", func() {
199+
getCruCardsFunc := configAccessObject["inventory"].(map[string]interface{})["CRUCardsForHost"].(func(string) string)
200+
Expect(getCruCardsFunc("flp001")).To(Equal("[\"0228\",\"0229\"]"))
201+
Expect(getCruCardsFunc("NOPE")).To(ContainSubstring("error"))
202+
})
203+
It("should get endpoints for CRU card", func() {
204+
getEndpointsFunc := configAccessObject["inventory"].(map[string]interface{})["EndpointsForCRUCard"].(func(string, string) string)
205+
Expect(getEndpointsFunc("flp001", "0228")).To(Equal("0 1"))
206+
// fixme: probably incorrect behaviour, but I don't want to risk breaking something
207+
Expect(getEndpointsFunc("flp001", "NOPE")).To(BeEmpty())
208+
})
209+
})
210+
})

0 commit comments

Comments
 (0)