From 88def03d5efde27f4a3bf0b69d1e1c5e7a0f2d5c4db6f71b595547c47d0f9262 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Mar=C3=ADn?= Date: Fri, 23 Jan 2026 13:18:01 +0100 Subject: [PATCH 1/2] Implement initial test-run option (#113) --- obs-status-service/main.go | 4 ++++ obs-status-service/redis.go | 41 +++++++++++++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/obs-status-service/main.go b/obs-status-service/main.go index 80446fe..15a632e 100644 --- a/obs-status-service/main.go +++ b/obs-status-service/main.go @@ -243,6 +243,7 @@ func main() { disableTls := flag.Bool("no-tls", false, "Disable TLS") ObsUrl = flag.String("obs-url", obsUrlDef, "OBS API endpoint for package buildlog information") debug := flag.Bool("debug", false, "Enable debug logging") + testrun := flag.Bool("test-run", false, "Run in test mode. Will use a fake REDIS server with mock data") flag.Parse() if *debug { @@ -251,6 +252,9 @@ func main() { if redisUrl := os.Getenv("REDIS"); len(redisUrl) > 0 { RedisConnect(redisUrl) + } else if *testrun { + RedisMock() + common.LogInfo("Running in TEST MODE, MockRedisConnect will be used") } else { common.LogError("REDIS needs to contains URL of the OBS Redis instance with login information") return diff --git a/obs-status-service/redis.go b/obs-status-service/redis.go index 89daf0c..83f43b9 100644 --- a/obs-status-service/redis.go +++ b/obs-status-service/redis.go @@ -15,6 +15,33 @@ var RepoStatus []*common.BuildResult = []*common.BuildResult{} var RepoStatusLock *sync.RWMutex = &sync.RWMutex{} var redisClient *redis.Client +var mock bool = false + +// Redis client with mock data for testing purposes +func RedisMock() { + mock = true +} + +func mockScanType() []string { + return []string{ + "result.devel:languages:python:Factory/openSUSE_Tumbleweed/x86_64", + "result.devel:Factory:git-workflow/openSUSE_Tumbleweed/x86_64", + } +} + +func mockHGetAll(key string) map[string]string { + data := map[string](map[string]string) { + "result.devel:languages:python:Factory/openSUSE_Tumbleweed/x86_64": map[string]string{ + "python313": "succeeded", + "python314": "failed", + }, + "result.devel:Factory:git-workflow/openSUSE_Tumbleweed/x86_64": map[string]string{ + "autogits": "succeeded", + }, + } + + return data[key] +} func RedisConnect(RedisUrl string) { opts, err := redis.ParseURL(RedisUrl) @@ -34,7 +61,13 @@ func UpdateResults(r *common.BuildResult) { func updateResultsWithoutLocking(r *common.BuildResult) { key := "result." + r.Project + "/" + r.Repository + "/" + r.Arch - data, err := redisClient.HGetAll(context.Background(), key).Result() + var data map[string]string + var err error + if mock { + data, err = mockHGetAll(key), nil + } else { + data, err = redisClient.HGetAll(context.Background(), key).Result() + } if err != nil { common.LogError("Failed fetching build results for", key, err) } @@ -200,7 +233,11 @@ func RescanRepositories() error { for { var data []string - data, cursor, err = redisClient.ScanType(ctx, cursor, "", 1000, "hash").Result() + if mock { + data, cursor, err = mockScanType(), 0, nil + } else { + data, cursor, err = redisClient.ScanType(ctx, cursor, "", 1000, "hash").Result() + } if err != nil { return err -- 2.51.1 From 1728c346f36facb95f38e5e892c5abf03eb567ef6cd5594f7a3aa3ef8056d6e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Mar=C3=ADn?= Date: Wed, 28 Jan 2026 23:41:28 +0100 Subject: [PATCH 2/2] refactor: Implement RedisClient interface for mocking strategy --- obs-status-service/redis.go | 49 ++++++++------------------------- obs-status-service/redismock.go | 43 +++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 37 deletions(-) create mode 100644 obs-status-service/redismock.go diff --git a/obs-status-service/redis.go b/obs-status-service/redis.go index 83f43b9..7e171ee 100644 --- a/obs-status-service/redis.go +++ b/obs-status-service/redis.go @@ -14,33 +14,18 @@ import ( var RepoStatus []*common.BuildResult = []*common.BuildResult{} var RepoStatusLock *sync.RWMutex = &sync.RWMutex{} -var redisClient *redis.Client -var mock bool = false +// Interface to get just the methods from redis.Client that we are +// using. This makes it possible to mock the redis for testing +// purposes +type RedisClient interface { + HGetAll(ctx context.Context, key string) *redis.MapStringStringCmd + ScanType(ctx context.Context, cursor uint64, match string, count int64, keyType string) *redis.ScanCmd +} + +var redisClient RedisClient -// Redis client with mock data for testing purposes func RedisMock() { - mock = true -} - -func mockScanType() []string { - return []string{ - "result.devel:languages:python:Factory/openSUSE_Tumbleweed/x86_64", - "result.devel:Factory:git-workflow/openSUSE_Tumbleweed/x86_64", - } -} - -func mockHGetAll(key string) map[string]string { - data := map[string](map[string]string) { - "result.devel:languages:python:Factory/openSUSE_Tumbleweed/x86_64": map[string]string{ - "python313": "succeeded", - "python314": "failed", - }, - "result.devel:Factory:git-workflow/openSUSE_Tumbleweed/x86_64": map[string]string{ - "autogits": "succeeded", - }, - } - - return data[key] + redisClient = &MockRedis{} } func RedisConnect(RedisUrl string) { @@ -61,13 +46,7 @@ func UpdateResults(r *common.BuildResult) { func updateResultsWithoutLocking(r *common.BuildResult) { key := "result." + r.Project + "/" + r.Repository + "/" + r.Arch - var data map[string]string - var err error - if mock { - data, err = mockHGetAll(key), nil - } else { - data, err = redisClient.HGetAll(context.Background(), key).Result() - } + data, err := redisClient.HGetAll(context.Background(), key).Result() if err != nil { common.LogError("Failed fetching build results for", key, err) } @@ -233,11 +212,7 @@ func RescanRepositories() error { for { var data []string - if mock { - data, cursor, err = mockScanType(), 0, nil - } else { - data, cursor, err = redisClient.ScanType(ctx, cursor, "", 1000, "hash").Result() - } + data, cursor, err = redisClient.ScanType(ctx, cursor, "", 1000, "hash").Result() if err != nil { return err diff --git a/obs-status-service/redismock.go b/obs-status-service/redismock.go new file mode 100644 index 0000000..8303413 --- /dev/null +++ b/obs-status-service/redismock.go @@ -0,0 +1,43 @@ +package main + +import ( + "context" + + "github.com/redis/go-redis/v9" +) + +type MockRedis struct {} + +// Implement RedisClient interface +// TODO: Read mock data from filesystem +func (m *MockRedis) HGetAll(ctx context.Context, key string) *redis.MapStringStringCmd { + python := map[string]string{ + "python313": "succeeded", + "python314": "failed", + } + + autogits := map[string]string { + "autogits": "succeeded", + } + + // result.PROJECT/REPO/ARCH -> packages build info + data := map[string](map[string]string) { + "result.devel:languages:python:Factory/openSUSE_Tumbleweed/x86_64": python, + "result.devel:Factory:git-workflow/openSUSE_Tumbleweed/x86_64": autogits, + } + + cmd := redis.NewMapStringStringCmd(ctx, "hgetall", data[key]) + cmd.SetVal(data[key]) + return cmd +} + +func (m *MockRedis) ScanType(ctx context.Context, cursor uint64, match string, count int64, keyType string) *redis.ScanCmd { + projects := []string{ + "result.devel:languages:python:Factory/openSUSE_Tumbleweed/x86_64", + "result.devel:Factory:git-workflow/openSUSE_Tumbleweed/x86_64", + } + + cmd := redis.NewScanCmd(ctx, nil) + cmd.SetVal(projects, 0) + return cmd +} -- 2.51.1