Skip to content

Commit 5edcea4

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

File tree

9 files changed

+425
-10
lines changed

9 files changed

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

0 commit comments

Comments
 (0)