Skip to content

Commit 154a8af

Browse files
Merge pull request #268 from SUSE/feature-addregCodeWays
Feature addreg code ways
2 parents d818538 + 8943dd7 commit 154a8af

File tree

2 files changed

+327
-7
lines changed

2 files changed

+327
-7
lines changed

cmd/suseconnect/suseconnect.go

+47-7
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
package main
22

33
import (
4+
"bufio"
45
_ "embed"
56
"encoding/json"
67
"errors"
78
"flag"
89
"fmt"
10+
"github.com/SUSE/connect-ng/internal/connect"
11+
"github.com/SUSE/connect-ng/internal/util"
12+
"github.com/SUSE/connect-ng/internal/zypper"
13+
"io"
914
"net/http"
1015
"net/url"
1116
"os"
1217
"runtime"
1318
"strings"
1419
"syscall"
15-
16-
"github.com/SUSE/connect-ng/internal/connect"
17-
"github.com/SUSE/connect-ng/internal/util"
18-
"github.com/SUSE/connect-ng/internal/zypper"
1920
)
2021

2122
var (
@@ -150,9 +151,7 @@ func main() {
150151
connect.CFG.Namespace = namespace
151152
writeConfig = true
152153
}
153-
if token != "" {
154-
connect.CFG.Token = token
155-
}
154+
parseRegistrationToken(token)
156155
if product.isSet {
157156
if p, err := connect.SplitTriplet(product.value); err != nil {
158157
fmt.Print("Please provide the product identifier in this format: ")
@@ -316,6 +315,18 @@ func main() {
316315
}
317316
}
318317

318+
func parseRegistrationToken(token string) {
319+
if token != "" {
320+
connect.CFG.Token = token
321+
processedToken, processTokenErr := processToken(token)
322+
if processTokenErr != nil {
323+
fmt.Printf("Error Processing token with error %+v", processTokenErr)
324+
os.Exit(1)
325+
}
326+
connect.CFG.Token = processedToken
327+
}
328+
}
329+
319330
func maybeBrokenSMTError() error {
320331
if !connect.CFG.IsScc() && !connect.UpToDate() {
321332
return fmt.Errorf("Your Registration Proxy server doesn't support this function. " +
@@ -403,3 +414,32 @@ func fileExists(path string) bool {
403414
func isSumaManaged() bool {
404415
return fileExists("/etc/sysconfig/rhn/systemid")
405416
}
417+
418+
func processToken(token string) (string, error) {
419+
if strings.HasPrefix(token, "@") {
420+
tokenFilePath := strings.TrimPrefix(token, "@")
421+
file, err := os.Open(tokenFilePath)
422+
if err != nil {
423+
return "", fmt.Errorf("failed to open token file '%s': %w", tokenFilePath, err)
424+
}
425+
defer file.Close()
426+
return readTokenFromReader(file)
427+
} else if token == "-" {
428+
return readTokenFromReader(os.Stdin)
429+
} else {
430+
return token, nil
431+
}
432+
}
433+
434+
func readTokenFromReader(reader io.Reader) (string, error) {
435+
bufReader := bufio.NewReader(reader)
436+
tokenBytes, err := bufReader.ReadString('\n')
437+
if err != nil && err != io.EOF {
438+
return "", fmt.Errorf("failed to read token from reader: %w", err)
439+
}
440+
token := strings.TrimSpace(tokenBytes)
441+
if token == "" {
442+
return "", fmt.Errorf("error: token cannot be empty after reading")
443+
}
444+
return token, nil
445+
}

cmd/suseconnect/suseconnect_test.go

+280
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
package main
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"github.com/SUSE/connect-ng/internal/connect"
7+
"github.com/SUSE/connect-ng/internal/util"
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/mock"
10+
"os"
11+
"strings"
12+
"testing"
13+
)
14+
15+
var processTokenFunc = processToken
16+
17+
var exitCalled bool
18+
var exit = func(code int) {
19+
exitCalled = true
20+
}
21+
22+
type MockProcessToken struct {
23+
mock.Mock
24+
}
25+
26+
func (m *MockProcessToken) ProcessToken(token string) (string, error) {
27+
args := m.Called(token)
28+
return args.String(0), args.Error(1)
29+
}
30+
31+
func init() {
32+
processTokenFunc = processToken
33+
}
34+
35+
type errorReader struct{}
36+
37+
func (e *errorReader) Read(p []byte) (n int, err error) {
38+
return 0, fmt.Errorf("forced reader error")
39+
}
40+
41+
func TestReadTokenFromErrorValidToken(t *testing.T) {
42+
inputToken := "validToken\n"
43+
reader := strings.NewReader(inputToken)
44+
token, err := readTokenFromReader(reader)
45+
if err != nil {
46+
t.Fatalf("Expected no error but got %v", err)
47+
}
48+
if token != "validToken" {
49+
t.Fatalf("Expected token string to be 'validToken' but got '%s'", token)
50+
}
51+
}
52+
53+
func TestReadTokenFromReader_MultipleNewlines(t *testing.T) {
54+
input := "firstToken\nsecondToken\n"
55+
reader := strings.NewReader(input)
56+
57+
token, err := readTokenFromReader(reader)
58+
if err != nil {
59+
t.Fatalf("Expected no error, but got %v", err)
60+
}
61+
62+
expected := "firstToken"
63+
if token != expected {
64+
t.Errorf("Expected token to be '%s', but got '%s'", expected, token)
65+
}
66+
}
67+
68+
func TestReadTokenFromReader_EmptyInput(t *testing.T) {
69+
reader := strings.NewReader("")
70+
71+
token, err := readTokenFromReader(reader)
72+
if err == nil {
73+
t.Fatalf("Expected error, but got none")
74+
}
75+
76+
expectedError := "error: token cannot be empty after reading"
77+
if err.Error() != expectedError {
78+
t.Errorf("Expected error '%s', but got '%s'", expectedError, err.Error())
79+
}
80+
81+
if token != "" {
82+
t.Errorf("Expected empty token, but got '%s'", token)
83+
}
84+
}
85+
86+
func TestReadTokenFromReader_OnlyNewline(t *testing.T) {
87+
reader := strings.NewReader("\n")
88+
89+
token, err := readTokenFromReader(reader)
90+
if err == nil {
91+
t.Fatalf("Expected error, but got none")
92+
}
93+
94+
expectedError := "error: token cannot be empty after reading"
95+
if err.Error() != expectedError {
96+
t.Errorf("Expected error '%s', but got '%s'", expectedError, err.Error())
97+
}
98+
99+
if token != "" {
100+
t.Errorf("Expected empty token, but got '%s'", token)
101+
}
102+
}
103+
104+
func TestReadTokenFromReader_ErrorProducingReader(t *testing.T) {
105+
reader := &errorReader{}
106+
107+
token, err := readTokenFromReader(reader)
108+
if err == nil {
109+
t.Fatalf("Expected error, but got none")
110+
}
111+
112+
expectedError := "failed to read token from reader: forced reader error"
113+
if err.Error() != expectedError {
114+
t.Errorf("Expected error '%s', but got '%s'", expectedError, err.Error())
115+
}
116+
117+
if token != "" {
118+
t.Errorf("Expected empty token, but got '%s'", token)
119+
}
120+
}
121+
122+
func TestProcessToken_RegularToken(t *testing.T) {
123+
token := "myRegularToken"
124+
result, err := processToken(token)
125+
if err != nil {
126+
t.Fatalf("Expected no error, but got %v", err)
127+
}
128+
129+
if result != token {
130+
t.Errorf("Expected token to be '%s', but got '%s'", token, result)
131+
}
132+
}
133+
134+
func TestProcessToken_TokenFromFile(t *testing.T) {
135+
tmpFile, err := os.CreateTemp("", "tokenfile")
136+
if err != nil {
137+
t.Fatalf("Failed to create temp file: %v", err)
138+
}
139+
defer os.Remove(tmpFile.Name())
140+
141+
expectedToken := "fileToken\n"
142+
if _, err := tmpFile.WriteString(expectedToken); err != nil {
143+
t.Fatalf("Failed to write to temp file: %v", err)
144+
}
145+
tmpFile.Close()
146+
147+
token := "@" + tmpFile.Name()
148+
result, err := processToken(token)
149+
if err != nil {
150+
t.Fatalf("Expected no error, but got %v", err)
151+
}
152+
153+
expectedToken = strings.TrimSpace(expectedToken)
154+
if result != expectedToken {
155+
t.Errorf("Expected token to be '%s', but got '%s'", expectedToken, result)
156+
}
157+
}
158+
159+
func TestProcessToken_NonExistentFile(t *testing.T) {
160+
token := "@/non/existent/file"
161+
_, err := processToken(token)
162+
if err == nil {
163+
t.Fatalf("Expected error for non-existent file, but got none")
164+
}
165+
166+
expectedError := "failed to open token file '/non/existent/file'"
167+
if !strings.Contains(err.Error(), expectedError) {
168+
t.Errorf("Expected error containing '%s', but got '%v'", expectedError, err)
169+
}
170+
}
171+
172+
func TestProcessToken_TokenFromStdin(t *testing.T) {
173+
tempFile, err := os.CreateTemp("", "test_stdin")
174+
if err != nil {
175+
t.Fatalf("Failed to create temp file: %v", err)
176+
}
177+
defer os.Remove(tempFile.Name())
178+
expectedToken := "stdinToken\n"
179+
if _, err := tempFile.WriteString(expectedToken); err != nil {
180+
t.Fatalf("Failed to write to temp file: %v", err)
181+
}
182+
183+
tempFile.Close()
184+
185+
oldStdin := os.Stdin
186+
defer func() { os.Stdin = oldStdin }()
187+
file, err := os.Open(tempFile.Name())
188+
if err != nil {
189+
t.Fatalf("Failed to open temp file: %v", err)
190+
}
191+
os.Stdin = file
192+
193+
result, err := processToken("-")
194+
if err != nil {
195+
t.Fatalf("Expected no error, but got %v", err)
196+
}
197+
198+
expectedToken = strings.TrimSpace(expectedToken)
199+
if result != expectedToken {
200+
t.Errorf("Expected token to be '%s', but got '%s'", expectedToken, result)
201+
}
202+
}
203+
204+
func TestProcessToken_ErrorReadingStdin(t *testing.T) {
205+
tempFile, err := os.CreateTemp("", "test_stdin_empty")
206+
if err != nil {
207+
t.Fatalf("Failed to create temp file: %v", err)
208+
}
209+
defer os.Remove(tempFile.Name())
210+
211+
tempFile.Close()
212+
oldStdin := os.Stdin
213+
defer func() { os.Stdin = oldStdin }()
214+
file, err := os.Open(tempFile.Name())
215+
if err != nil {
216+
t.Fatalf("Failed to open temp file: %v", err)
217+
}
218+
os.Stdin = file
219+
220+
_, err = processToken("-")
221+
if err == nil {
222+
t.Fatalf("Expected error reading from stdin, but got none")
223+
}
224+
225+
expectedError := "error: token cannot be empty after reading"
226+
if !strings.Contains(err.Error(), expectedError) {
227+
t.Errorf("Expected error containing '%s', but got '%v'", expectedError, err)
228+
}
229+
}
230+
231+
func parseRegistrationTokenWithInjection(token string) {
232+
if token != "" {
233+
connect.CFG.Token = token
234+
processedToken, processTokenErr := processTokenFunc(token)
235+
if processTokenErr != nil {
236+
util.Debug.Printf("Error Processing token %+v", processTokenErr)
237+
exit(1)
238+
}
239+
connect.CFG.Token = processedToken
240+
}
241+
}
242+
243+
func TestParseToken_Success(t *testing.T) {
244+
mockProcessToken := new(MockProcessToken)
245+
mockProcessToken.On("ProcessToken", "valid-token").Return("processed-token", nil)
246+
247+
processTokenFunc = mockProcessToken.ProcessToken
248+
249+
exitCalled = false
250+
251+
parseRegistrationTokenWithInjection("valid-token")
252+
253+
assert.Equal(t, "processed-token", connect.CFG.Token, "Token should be processed correctly")
254+
assert.False(t, exitCalled, "os.Exit (simulated) should not be called in a successful case")
255+
256+
mockProcessToken.AssertExpectations(t)
257+
}
258+
259+
func TestParseToken_ProcessTokenError(t *testing.T) {
260+
mockProcessToken := new(MockProcessToken)
261+
mockProcessToken.On("ProcessToken", "invalid-token").Return("", errors.New("failed to process token"))
262+
263+
processTokenFunc = mockProcessToken.ProcessToken
264+
265+
exitCalled = false
266+
267+
parseRegistrationTokenWithInjection("invalid-token")
268+
269+
assert.True(t, exitCalled, "os.Exit (simulated) should be called when processToken fails")
270+
assert.Equal(t, "", connect.CFG.Token, "Token should not be updated when processToken fails")
271+
272+
mockProcessToken.AssertExpectations(t)
273+
}
274+
275+
func TestParseToken_EmptyToken(t *testing.T) {
276+
exitCalled = false
277+
parseRegistrationTokenWithInjection("")
278+
assert.Empty(t, connect.CFG.Token, "Token should not be updated when input token is empty")
279+
assert.False(t, exitCalled, "os.Exit (simulated) should not be called for empty token")
280+
}

0 commit comments

Comments
 (0)