Skip to content

Commit 876fcdb

Browse files
committed
Add unit tests for telemetry coverage > 80%
1 parent d330990 commit 876fcdb

File tree

1 file changed

+177
-0
lines changed

1 file changed

+177
-0
lines changed

pkg/telemetry/telemetry_test.go

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package telemetry
16+
17+
import (
18+
"hpc-toolkit/pkg/shell"
19+
"os"
20+
"path/filepath"
21+
"sync"
22+
"testing"
23+
)
24+
25+
func TestIsInternalUser(t *testing.T) {
26+
orig := shell.ExecuteCommand
27+
defer func() { shell.ExecuteCommand = orig }()
28+
29+
tests := []struct {
30+
name string
31+
mockStdout string
32+
mockExitCode int
33+
expectedResult bool
34+
}{
35+
{
36+
name: "Internal User Google",
37+
mockStdout: "testuser@google.com\n",
38+
mockExitCode: 0,
39+
expectedResult: true,
40+
},
41+
{
42+
name: "External User",
43+
mockStdout: "testuser@example.com\n",
44+
mockExitCode: 0,
45+
expectedResult: false,
46+
},
47+
{
48+
name: "Command Error",
49+
mockStdout: "",
50+
mockExitCode: 1,
51+
expectedResult: false,
52+
},
53+
}
54+
55+
for _, tt := range tests {
56+
t.Run(tt.name, func(t *testing.T) {
57+
// Reset sync.Once and cached value for each test case
58+
isInternalOnce = sync.Once{}
59+
isInternalCached = false
60+
61+
shell.ExecuteCommand = func(name string, args ...string) shell.CommandResult {
62+
return shell.CommandResult{Stdout: tt.mockStdout, ExitCode: tt.mockExitCode}
63+
}
64+
65+
got := isInternalUser()
66+
if got != tt.expectedResult {
67+
t.Errorf("isInternalUser() = %v, want %v", got, tt.expectedResult)
68+
}
69+
})
70+
}
71+
}
72+
73+
func TestRecordLocalMetrics_Skip(t *testing.T) {
74+
os.Setenv("GCLUSTER_SKIP_TELEMETRY", "true")
75+
defer os.Unsetenv("GCLUSTER_SKIP_TELEMETRY")
76+
77+
// If it skips, it shouldn't access file system or call gcloud
78+
RecordLocalMetrics("test-job", 1.0, true, nil)
79+
// Success if it doesn't panic or error out (since we aren't verify file output here yet, but cover the early exit)
80+
}
81+
82+
func TestRecordLocalMetrics_ExternalUser(t *testing.T) {
83+
orig := shell.ExecuteCommand
84+
defer func() { shell.ExecuteCommand = orig }()
85+
86+
// For external user, it should skip file writing
87+
isInternalOnce = sync.Once{}
88+
isInternalCached = false
89+
shell.ExecuteCommand = func(name string, args ...string) shell.CommandResult {
90+
return shell.CommandResult{Stdout: "external@example.com\n", ExitCode: 0}
91+
}
92+
93+
RecordLocalMetrics("test-job", 1.0, true, nil)
94+
// Success if it exits quietly
95+
}
96+
97+
func TestRecordLocalMetrics_Success(t *testing.T) {
98+
tempDir, err := os.MkdirTemp("", "telemetry-test")
99+
if err != nil {
100+
t.Fatal(err)
101+
}
102+
defer os.RemoveAll(tempDir)
103+
104+
origHome := os.Getenv("HOME")
105+
os.Setenv("HOME", tempDir)
106+
defer os.Setenv("HOME", origHome)
107+
108+
orig := shell.ExecuteCommand
109+
defer func() { shell.ExecuteCommand = orig }()
110+
111+
isInternalOnce = sync.Once{}
112+
isInternalCached = false
113+
shell.ExecuteCommand = func(name string, args ...string) shell.CommandResult {
114+
return shell.CommandResult{Stdout: "internal@google.com\n", ExitCode: 0}
115+
}
116+
117+
RecordLocalMetrics("test-job", 1.0, true, map[string]string{"foo": "bar"})
118+
119+
// Verify file was written
120+
metricsFile := filepath.Join(tempDir, ".gcluster-job", "telemetry_metrics.jsonl")
121+
if _, err := os.Stat(metricsFile); os.IsNotExist(err) {
122+
t.Errorf("expected file %s to exist", metricsFile)
123+
}
124+
}
125+
126+
func TestRecordLocalMetrics_MkdirError(t *testing.T) {
127+
origHome := os.Getenv("HOME")
128+
os.Setenv("HOME", "/dev/null") // Cannot create directories inside /dev/null
129+
defer os.Setenv("HOME", origHome)
130+
131+
orig := shell.ExecuteCommand
132+
defer func() { shell.ExecuteCommand = orig }()
133+
134+
isInternalOnce = sync.Once{}
135+
isInternalCached = false
136+
shell.ExecuteCommand = func(name string, args ...string) shell.CommandResult {
137+
return shell.CommandResult{Stdout: "internal@google.com\n", ExitCode: 0}
138+
}
139+
140+
RecordLocalMetrics("test-job", 1.0, true, nil)
141+
// Success if it exits quietly after logging error
142+
}
143+
144+
func TestRecordLocalMetrics_FileOpenError(t *testing.T) {
145+
tempDir, err := os.MkdirTemp("", "telemetry-test")
146+
if err != nil {
147+
t.Fatal(err)
148+
}
149+
defer os.RemoveAll(tempDir)
150+
151+
metricsDir := filepath.Join(tempDir, ".gcluster-job")
152+
if err := os.MkdirAll(metricsDir, 0755); err != nil {
153+
t.Fatal(err)
154+
}
155+
156+
// Make directory read-only to force open file error
157+
if err := os.Chmod(metricsDir, 0555); err != nil {
158+
t.Fatal(err)
159+
}
160+
defer os.Chmod(metricsDir, 0755) // Ensure cleanup works
161+
162+
origHome := os.Getenv("HOME")
163+
os.Setenv("HOME", tempDir)
164+
defer os.Setenv("HOME", origHome)
165+
166+
orig := shell.ExecuteCommand
167+
defer func() { shell.ExecuteCommand = orig }()
168+
169+
isInternalOnce = sync.Once{}
170+
isInternalCached = false
171+
shell.ExecuteCommand = func(name string, args ...string) shell.CommandResult {
172+
return shell.CommandResult{Stdout: "internal@google.com\n", ExitCode: 0}
173+
}
174+
175+
RecordLocalMetrics("test-job", 1.0, true, nil)
176+
// Success if it exits quietly after logging error
177+
}

0 commit comments

Comments
 (0)