CI: Migrate Sonar Scan job to GHA
[nonrtric.git] / auth-token-fetch / main_test.go
1 // -
2 //   ========================LICENSE_START=================================
3 //   O-RAN-SC
4 //   %%
5 //   Copyright (C) 2022: Nordix Foundation
6 //   %%
7 //   Licensed under the Apache License, Version 2.0 (the "License");
8 //   you may not use this file except in compliance with the License.
9 //   You may obtain a copy of the License at
10 //
11 //        http://www.apache.org/licenses/LICENSE-2.0
12 //
13 //   Unless required by applicable law or agreed to in writing, software
14 //   distributed under the License is distributed on an "AS IS" BASIS,
15 //   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 //   See the License for the specific language governing permissions and
17 //   limitations under the License.
18 //   ========================LICENSE_END===================================
19 //
20
21 package main
22
23 import (
24         "bytes"
25         "encoding/json"
26         "errors"
27         "fmt"
28         "io/ioutil"
29         "net/http"
30         "os"
31         "testing"
32         "time"
33
34         log "github.com/sirupsen/logrus"
35         "github.com/stretchr/testify/require"
36 )
37
38 func createHttpClientMock(t *testing.T, configuration *Config, token JwtToken) *http.Client {
39         assertions := require.New(t)
40         clientMock := NewTestClient(func(req *http.Request) *http.Response {
41                 if req.URL.String() == configuration.AuthServiceUrl {
42                         assertions.Equal(req.Method, "POST")
43                         body := getBodyAsString(req, t)
44                         assertions.Contains(body, "client_id="+configuration.ClientId)
45                         assertions.Contains(body, "secret="+configuration.ClientSecret)
46                         assertions.Contains(body, "grant_type="+configuration.GrantType)
47                         contentType := req.Header.Get("content-type")
48                         assertions.Equal("application/x-www-form-urlencoded", contentType)
49
50                         return &http.Response{
51                                 StatusCode: 200,
52                                 Body:       ioutil.NopCloser(bytes.NewBuffer(toBody(token))),
53                                 Header:     make(http.Header), // Must be set to non-nil value or it panics
54                         }
55                 }
56                 t.Error("Wrong call to client: ", req)
57                 t.Fail()
58                 return nil
59         })
60         return clientMock
61 }
62
63 func TestFetchAndStoreToken(t *testing.T) {
64         log.SetLevel(log.TraceLevel)
65         assertions := require.New(t)
66         configuration := NewConfig()
67         configuration.AuthTokenOutputFileName = "/tmp/authToken" + fmt.Sprint(time.Now().UnixNano())
68         configuration.ClientId = "testClientId"
69         configuration.ClientSecret = "testClientSecret"
70         configuration.RefreshMarginSeconds = 1
71         context := NewContext(configuration)
72
73         t.Cleanup(func() {
74                 os.Remove(configuration.AuthTokenOutputFileName)
75         })
76
77         accessToken := "Access_token" + fmt.Sprint(time.Now().UnixNano())
78         token := JwtToken{Access_token: accessToken, Expires_in: 7, Token_type: "Token_type"}
79
80         clientMock := createHttpClientMock(t, configuration, token)
81
82         go periodicRefreshIwtToken(clientMock, context)
83
84         await(func() bool { return fileExists(configuration.AuthTokenOutputFileName) }, t)
85
86         tokenFileContent, err := ioutil.ReadFile(configuration.AuthTokenOutputFileName)
87         check(err)
88
89         assertions.Equal(accessToken, string(tokenFileContent))
90
91         context.Running = false
92 }
93
94 func fileExists(fileName string) bool {
95         if _, err := os.Stat(fileName); err == nil {
96                 return true
97         }
98         log.Debug("Waiting for file: " + fileName)
99         return false
100 }
101
102 func await(predicate func() bool, t *testing.T) {
103         MAX_TIME_SECONDS := 30
104         for i := 1; i < MAX_TIME_SECONDS; i++ {
105                 if predicate() {
106                         return
107                 }
108                 time.Sleep(time.Second)
109         }
110         t.Error("Predicate not fulfilled")
111         t.Fail()
112 }
113
114 func TestStart(t *testing.T) {
115         assertions := require.New(t)
116         log.SetLevel(log.TraceLevel)
117
118         configuration := NewConfig()
119         configuration.AuthTokenOutputFileName = "/tmp/authToken" + fmt.Sprint(time.Now().UnixNano())
120         configuration.CACertsPath = configuration.CertPath
121         context := NewContext(configuration)
122         t.Cleanup(func() {
123                 os.Remove(configuration.AuthTokenOutputFileName)
124         })
125
126         start(context)
127
128         time.Sleep(time.Second * 5)
129
130         _, err := os.Stat(configuration.AuthTokenOutputFileName)
131
132         assertions.True(errors.Is(err, os.ErrNotExist))
133         context.Running = false
134 }
135
136 func toBody(token JwtToken) []byte {
137         body, err := json.Marshal(token)
138         check(err)
139         return body
140 }
141
142 type RoundTripFunc func(req *http.Request) *http.Response
143
144 func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
145         return f(req), nil
146 }
147
148 //NewTestClient returns *http.Client with Transport replaced to avoid making real calls
149 func NewTestClient(fn RoundTripFunc) *http.Client {
150         return &http.Client{
151                 Transport: RoundTripFunc(fn),
152         }
153 }
154
155 func getBodyAsString(req *http.Request, t *testing.T) string {
156         buf := new(bytes.Buffer)
157         if _, err := buf.ReadFrom(req.Body); err != nil {
158                 t.Fail()
159         }
160         return buf.String()
161 }