• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

bavix / gripmock / 17066567963

19 Aug 2025 10:15AM UTC coverage: 52.632% (-0.9%) from 53.531%
17066567963

push

github

web-flow
Merge pull request #653 from bavix/652-field-outputheaders-not-being-sent-as-trailers-if-outputcode-0

Fix Trailers

78 of 190 new or added lines in 8 files covered. (41.05%)

7 existing lines in 1 file now uncovered.

1590 of 3021 relevant lines covered (52.63%)

30.19 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

38.46
/internal/infra/watcher/stub.go
1
package watcher
2

3
import (
4
        "context"
5
        "io/fs"
6
        "os"
7
        "path/filepath"
8
        "slices"
9
        "strings"
10
        "time"
11

12
        "github.com/fsnotify/fsnotify"
13
        "github.com/gripmock/environment"
14
        "github.com/rs/zerolog"
15
)
16

17
type StubWatcher struct {
18
        enabled     bool
19
        interval    time.Duration
20
        watcherType string
21
}
22

23
func NewStubWatcher(
24
        cfg environment.Config,
25
) *StubWatcher {
1✔
26
        watcherType := string(cfg.StubWatcherType)
1✔
27

1✔
28
        if !slices.Contains(
1✔
29
                []string{
1✔
30
                        string(environment.WatcherFSNotify),
1✔
31
                        string(environment.WatcherTimer),
1✔
32
                },
1✔
33
                watcherType,
1✔
34
        ) {
1✔
35
                watcherType = string(environment.WatcherFSNotify)
×
36
        }
×
37

38
        return &StubWatcher{
1✔
39
                enabled:     cfg.StubWatcherEnabled,
1✔
40
                interval:    cfg.StubWatcherInterval,
1✔
41
                watcherType: watcherType,
1✔
42
        }
1✔
43
}
44

45
func (s *StubWatcher) Watch(ctx context.Context, folderPath string) (<-chan string, error) {
1✔
46
        if !s.enabled {
1✔
47
                ch := make(chan string)
×
48
                close(ch)
×
49

×
50
                return ch, nil
×
51
        }
×
52

53
        zerolog.Ctx(ctx).Info().
1✔
54
                Str("type", s.watcherType).
1✔
55
                Msg("Tracking changes in stubs")
1✔
56

1✔
57
        if s.watcherType == string(environment.WatcherFSNotify) {
2✔
58
                return s.notify(ctx, folderPath)
1✔
59
        }
1✔
60

61
        return s.ticker(ctx, folderPath)
×
62
}
63

64
func (s *StubWatcher) notify(ctx context.Context, folderPath string) (<-chan string, error) {
1✔
65
        watcher, err := fsnotify.NewWatcher()
1✔
66
        if err != nil {
1✔
67
                return nil, err //nolint:wrapcheck
×
68
        }
×
69

70
        ch := make(chan string)
1✔
71

1✔
72
        go func() {
2✔
73
                defer func() {
2✔
74
                        if r := recover(); r != nil {
1✔
NEW
75
                                zerolog.Ctx(ctx).
×
NEW
76
                                        Error().
×
NEW
77
                                        Interface("panic", r).
×
NEW
78
                                        Msg("Panic recovered in fsnotify watcher goroutine")
×
NEW
79
                        }
×
80

81
                        _ = watcher.Close()
1✔
82
                }()
83
                defer close(ch)
1✔
84

1✔
85
                for {
2✔
86
                        select {
1✔
87
                        case <-ctx.Done():
1✔
88
                                return
1✔
89
                        case event, ok := <-watcher.Events:
×
90
                                if !ok || event.Op == fsnotify.Chmod {
×
91
                                        continue
×
92
                                }
93

NEW
94
                                s.handleFsnotifyEvent(ctx, watcher, ch, event)
×
95
                        }
96
                }
97
        }()
98

99
        _ = filepath.Walk(folderPath, func(currentPath string, info fs.FileInfo, err error) error {
252✔
100
                if err != nil {
251✔
101
                        return err
×
102
                }
×
103

104
                if !info.IsDir() {
459✔
105
                        return nil
208✔
106
                }
208✔
107

108
                zerolog.Ctx(ctx).Err(watcher.Add(currentPath)).
43✔
109
                        Str("path", currentPath).
43✔
110
                        Msg("Adding directory to watcher")
43✔
111

43✔
112
                return nil
43✔
113
        })
114

115
        return ch, nil
1✔
116
}
117

118
func (s *StubWatcher) ticker(ctx context.Context, folderPath string) (<-chan string, error) {
×
119
        ch := make(chan string)
×
120

×
121
        stubFiles := make(map[string]time.Time)
×
122

×
123
        zerolog.Ctx(ctx).
×
124
                Info().
×
125
                Str("interval", s.interval.String()).
×
126
                Msg("Starting stub ticker watcher")
×
127

×
128
        go func() {
×
129
                ticker := time.NewTicker(s.interval)
×
130
                defer ticker.Stop()
×
131
                defer close(ch)
×
132

×
133
                for {
×
134
                        select {
×
135
                        case <-ctx.Done():
×
136
                                return
×
137
                        case <-ticker.C:
×
138
                                _ = filepath.Walk(folderPath, func(currentPath string, info fs.FileInfo, err error) error {
×
139
                                        if err != nil {
×
140
                                                return err
×
141
                                        }
×
142

143
                                        if info.IsDir() || isStub(currentPath) {
×
144
                                                return nil
×
145
                                        }
×
146

147
                                        if lastModifyTime, ok := stubFiles[currentPath]; ok && info.ModTime().Equal(lastModifyTime) {
×
148
                                                return nil
×
149
                                        }
×
150

151
                                        ch <- currentPath
×
152

×
153
                                        stubFiles[currentPath] = info.ModTime()
×
154

×
155
                                        return nil
×
156
                                })
157
                        }
158
                }
159
        }()
160

161
        return ch, nil
×
162
}
163

164
func isStub(path string) bool {
×
165
        return strings.HasSuffix(path, ".json") ||
×
166
                strings.HasSuffix(path, ".yaml") ||
×
167
                strings.HasSuffix(path, ".yml")
×
168
}
×
169

170
// handleFsnotifyEvent handles a single fsnotify event with panic recovery.
NEW
171
func (s *StubWatcher) handleFsnotifyEvent(ctx context.Context, watcher *fsnotify.Watcher, ch chan<- string, event fsnotify.Event) {
×
NEW
172
        defer func() {
×
NEW
173
                if r := recover(); r != nil {
×
NEW
174
                        zerolog.Ctx(ctx).
×
NEW
175
                                Error().
×
NEW
176
                                Interface("panic", r).
×
NEW
177
                                Str("file", event.Name).
×
NEW
178
                                Msg("Panic recovered while processing fsnotify event")
×
NEW
179
                }
×
180
        }()
181

NEW
182
        info, err := os.Stat(event.Name)
×
NEW
183
        if err == nil && info.IsDir() {
×
NEW
184
                zerolog.Ctx(ctx).Err(watcher.Add(event.Name)).
×
NEW
185
                        Str("path", event.Name).
×
NEW
186
                        Msg("Adding directory to watcher")
×
NEW
187
        }
×
188

NEW
189
        if isStub(event.Name) {
×
NEW
190
                ch <- event.Name
×
NEW
191
        }
×
192
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc