Repository navigation
Expand file tree
/
Copy pathparser_access_test.go
More file actions
109 lines (97 loc) · 3.25 KB
/
Copy pathparser_access_test.go
File metadata and controls
109 lines (97 loc) · 3.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package tinyconf
import (
"context"
"errors"
"fmt"
"sync"
"testing"
"time"
"github.com/insei/fmap/v3"
"github.com/stretchr/testify/assert"
)
// errorMockDriver returns the error for the field Denied, its Load and Watch return the errors from the fields.
type errorMockDriver struct {
loadErr error
watchErr error
}
func (md *errorMockDriver) GenDoc(...*Registered) string { return "" }
func (md *errorMockDriver) GetName() string { return "errors" }
func (md *errorMockDriver) Load() error { return md.loadErr }
func (md *errorMockDriver) GetValue(field fmap.Field) (*Value, error) {
switch field.GetStructPath() {
case "Denied":
return nil, fmt.Errorf("%w: no permission to read denied", ErrAccessDenied)
case "Failed":
return nil, errors.New("connection lost")
}
return &Value{Source: "key", Value: "value"}, nil
}
func (md *errorMockDriver) Watch(context.Context, func()) error {
return md.watchErr
}
// errorLogger records error messages with the details.
type errorLogger struct {
noopLogger
mu *sync.Mutex
errors *[]string
}
func (l *errorLogger) Error(msg string, fields ...Field) {
l.mu.Lock()
defer l.mu.Unlock()
for _, field := range fields {
if field.Key == "details" {
msg += ": " + field.Value.(string)
}
}
*l.errors = append(*l.errors, msg)
}
func (l *errorLogger) With(...Field) Logger { return l }
func TestManager_AccessDenied(t *testing.T) {
type Config struct {
Denied string
Failed string
}
var logged []string
log := &errorLogger{mu: &sync.Mutex{}, errors: &logged}
driver := &errorMockDriver{}
c, err := New(WithDriver(driver), WithLogger(log))
assert.NoError(t, err)
conf := &Config{}
assert.NoError(t, c.Register(conf))
// access denied is logged by its own message and can be checked by errors.Is
err = c.Parse(context.Background(), conf)
assert.ErrorIs(t, err, ErrAccessDenied)
assert.Equal(t, []string{
"access denied: access denied: no permission to read denied",
"failed: connection lost",
}, logged)
logged = nil
driver.loadErr = fmt.Errorf("%w: no permission to read the source", ErrAccessDenied)
assert.ErrorIs(t, c.Parse(context.Background(), conf), ErrAccessDenied)
driver.loadErr = errors.New("source is not available")
assert.Error(t, c.Parse(context.Background(), conf))
assert.Equal(t, []string{
"access denied: access denied: no permission to read the source",
"failed to load: source is not available",
}, logged)
for watchErr, message := range map[error]string{
fmt.Errorf("%w: no permission to watch", ErrAccessDenied): "access denied: access denied: no permission to watch",
errors.New("watching is failed"): "failed to watch: watching is failed",
} {
log.mu.Lock()
logged = nil
log.mu.Unlock()
// every case with its own manager, watching is started by the first subscription
c, err := New(WithDriver(&errorMockDriver{watchErr: watchErr}), WithLogger(log))
assert.NoError(t, err)
assert.NoError(t, c.Register(conf))
ctx, cancel := context.WithCancel(context.Background())
assert.NoError(t, Subscribe(ctx, c, func(prev, next *Config) {}))
assert.Eventually(t, func() bool {
log.mu.Lock()
defer log.mu.Unlock()
return len(logged) == 1 && logged[0] == message
}, 5*time.Second, 10*time.Millisecond)
cancel()
}
}