github.com/arduino/arduino-cloud-cli@v0.0.0-20240517070944-e7a449561083/config/credentials_test.go (about)

     1  // This file is part of arduino-cloud-cli.
     2  //
     3  // Copyright (C) 2021 ARDUINO SA (http://www.arduino.cc/)
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published
     7  // by the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <https://www.gnu.org/licenses/>.
    17  
    18  package config
    19  
    20  import (
    21  	"os"
    22  	"testing"
    23  
    24  	"encoding/json"
    25  
    26  	"github.com/google/go-cmp/cmp"
    27  )
    28  
    29  func TestRetrieveCredentials(t *testing.T) {
    30  	var (
    31  		validSecret             = "qaRZGEbnQNNvmaeTLqy8Bxs22wLZ6H7obIiNSveTLPdoQuylANnuy6WBOw16XoqH"
    32  		validClient             = "CQ4iZ5sebOfhGRwUn3IV0r1YFMNrMTIx"
    33  		validOrganization       = "dc6a6159-3cd5-41a2-b391-553b1351cd98"
    34  		validConfig             = &Credentials{Client: validClient, Secret: validSecret}
    35  		validWithOptionalConfig = &Credentials{Client: validClient, Secret: validSecret, Organization: validOrganization}
    36  		invalidConfig           = &Credentials{Client: "", Secret: validSecret}
    37  		clientEnv               = EnvPrefix + "_CLIENT"
    38  		secretEnv               = EnvPrefix + "_SECRET"
    39  		organizationEnv         = EnvPrefix + "_ORGANIZATION"
    40  	)
    41  
    42  	tests := []struct {
    43  		name         string
    44  		pre          func()
    45  		post         func()
    46  		wantedConfig *Credentials
    47  		wantedErr    bool
    48  	}{
    49  		{
    50  			name: "valid credentials with only mandatory params written in env",
    51  			pre: func() {
    52  				os.Setenv(clientEnv, validConfig.Client)
    53  				os.Setenv(secretEnv, validConfig.Secret)
    54  			},
    55  			post: func() {
    56  				os.Unsetenv(clientEnv)
    57  				os.Unsetenv(secretEnv)
    58  			},
    59  			wantedConfig: validConfig,
    60  			wantedErr:    false,
    61  		},
    62  
    63  		{
    64  			name: "valid credentials with optional params written in env",
    65  			pre: func() {
    66  				os.Setenv(clientEnv, validWithOptionalConfig.Client)
    67  				os.Setenv(secretEnv, validWithOptionalConfig.Secret)
    68  				os.Setenv(organizationEnv, validWithOptionalConfig.Organization)
    69  			},
    70  			post: func() {
    71  				os.Unsetenv(clientEnv)
    72  				os.Unsetenv(secretEnv)
    73  				os.Unsetenv(organizationEnv)
    74  			},
    75  			wantedConfig: validWithOptionalConfig,
    76  			wantedErr:    false,
    77  		},
    78  
    79  		{
    80  			name: "invalid credentials written in env",
    81  			pre: func() {
    82  				os.Setenv(clientEnv, validConfig.Client)
    83  				os.Setenv(secretEnv, "")
    84  			},
    85  			post: func() {
    86  				os.Unsetenv(clientEnv)
    87  				os.Unsetenv(secretEnv)
    88  			},
    89  			wantedConfig: nil,
    90  			wantedErr:    true,
    91  		},
    92  
    93  		{
    94  			name: "valid credentials written in parent of cwd",
    95  			pre: func() {
    96  				parent := "test-parent"
    97  				cwd := "test-parent/test-cwd"
    98  				os.MkdirAll(cwd, os.FileMode(0777))
    99  				// Write valid credentials in parent dir
   100  				os.Chdir(parent)
   101  				b, _ := json.Marshal(validConfig)
   102  				os.WriteFile(CredentialsFilename+".json", b, os.FileMode(0777))
   103  				// Cwd has no credentials file
   104  				os.Chdir("test-cwd")
   105  			},
   106  			post: func() {
   107  				os.Chdir("../..")
   108  				os.RemoveAll("test-parent")
   109  			},
   110  			wantedConfig: validConfig,
   111  			wantedErr:    false,
   112  		},
   113  
   114  		{
   115  			name: "valid credentials with optional params written in parent of cwd",
   116  			pre: func() {
   117  				parent := "test-parent"
   118  				cwd := "test-parent/test-cwd"
   119  				os.MkdirAll(cwd, os.FileMode(0777))
   120  				// Write valid credentials in parent dir
   121  				os.Chdir(parent)
   122  				b, _ := json.Marshal(validWithOptionalConfig)
   123  				os.WriteFile(CredentialsFilename+".json", b, os.FileMode(0777))
   124  				// Cwd has no credentials file
   125  				os.Chdir("test-cwd")
   126  			},
   127  			post: func() {
   128  				os.Chdir("../..")
   129  				os.RemoveAll("test-parent")
   130  			},
   131  			wantedConfig: validWithOptionalConfig,
   132  			wantedErr:    false,
   133  		},
   134  
   135  		{
   136  			name: "invalid credentials written in cwd, ignore credentials of parent dir",
   137  			pre: func() {
   138  				parent := "test-parent"
   139  				cwd := "test-parent/test-cwd"
   140  				os.MkdirAll(cwd, os.FileMode(0777))
   141  				// Write valid credentials in parent dir
   142  				os.Chdir(parent)
   143  				b, _ := json.Marshal(validConfig)
   144  				os.WriteFile(CredentialsFilename+".json", b, os.FileMode(0777))
   145  				// Write invalid credentials in cwd
   146  				os.Chdir("test-cwd")
   147  				b, _ = json.Marshal(invalidConfig)
   148  				os.WriteFile(CredentialsFilename+".json", b, os.FileMode(0777))
   149  			},
   150  			post: func() {
   151  				os.Chdir("../..")
   152  				os.RemoveAll("test-parent")
   153  				os.Unsetenv(clientEnv)
   154  				os.Unsetenv(secretEnv)
   155  			},
   156  			wantedConfig: nil,
   157  			wantedErr:    true,
   158  		},
   159  
   160  		{
   161  			name: "invalid credentials written in env, ignore valid credentials of cwd",
   162  			pre: func() {
   163  				cwd := "test-cwd"
   164  				os.MkdirAll(cwd, os.FileMode(0777))
   165  				// Write valid credentials in cwd
   166  				os.Chdir(cwd)
   167  				b, _ := json.Marshal(validConfig)
   168  				os.WriteFile(CredentialsFilename+".json", b, os.FileMode(0777))
   169  				// Write invalid credentials in env
   170  				os.Setenv(clientEnv, validConfig.Client)
   171  				os.Setenv(secretEnv, "")
   172  			},
   173  			post: func() {
   174  				os.Chdir("..")
   175  				os.RemoveAll("test-cwd")
   176  			},
   177  			wantedConfig: nil,
   178  			wantedErr:    true,
   179  		},
   180  	}
   181  
   182  	for _, tt := range tests {
   183  		t.Run(tt.name, func(t *testing.T) {
   184  			tt.pre()
   185  			got, err := RetrieveCredentials()
   186  			tt.post()
   187  
   188  			if tt.wantedErr && err == nil {
   189  				t.Errorf("Expected an error, but got nil")
   190  			}
   191  			if !tt.wantedErr && err != nil {
   192  				t.Errorf("Expected nil error, but got: %v", err)
   193  			}
   194  
   195  			if !cmp.Equal(got, tt.wantedConfig) {
   196  				t.Errorf("Wrong credentials received, diff:\n%s", cmp.Diff(tt.wantedConfig, got))
   197  			}
   198  		})
   199  	}
   200  }
   201  
   202  func TestValidate(t *testing.T) {
   203  	var (
   204  		validSecret       = "qaRZGEbnQNNvmaeTLqy8Bxs22wLZ6H7obIiNSveTLPdoQuylANnuy6WBOw16XoqH"
   205  		validClient       = "CQ4iZ5sebOfhGRwUn3IV0r1YFMNrMTIx"
   206  		validOrganization = "dc6a6159-3cd5-41a2-b391-553b1351cd98"
   207  	)
   208  	tests := []struct {
   209  		name   string
   210  		config *Credentials
   211  		valid  bool
   212  	}{
   213  		{
   214  			name:   "valid credentials",
   215  			config: &Credentials{Client: validClient, Secret: validSecret, Organization: validOrganization},
   216  			valid:  true,
   217  		},
   218  		{
   219  			name:   "valid credentials, organization is optional",
   220  			config: &Credentials{Client: validClient, Secret: validSecret, Organization: ""},
   221  			valid:  true,
   222  		},
   223  		{
   224  			name:   "invalid client id",
   225  			config: &Credentials{Client: "", Secret: validSecret},
   226  			valid:  false,
   227  		},
   228  		{
   229  			name:   "invalid client secret",
   230  			config: &Credentials{Client: validClient, Secret: ""},
   231  			valid:  false,
   232  		},
   233  	}
   234  
   235  	for _, tt := range tests {
   236  		t.Run(tt.name, func(t *testing.T) {
   237  			err := tt.config.Validate()
   238  			if tt.valid && err != nil {
   239  				t.Errorf(
   240  					"Wrong validation, the credentials were correct but an error was received: \ncredentials: %v\nerr: %v",
   241  					tt.config,
   242  					err,
   243  				)
   244  			}
   245  			if !tt.valid && err == nil {
   246  				t.Errorf(
   247  					"Wrong validation, the credentials were invalid but no error was received: \ncredentials: %v",
   248  					tt.config,
   249  				)
   250  			}
   251  		})
   252  	}
   253  }
   254  
   255  func TestIsEmpty(t *testing.T) {
   256  	var (
   257  		validSecret       = "qaRZGEbnQNNvmaeTLqy8Bxs22wLZ6H7obIiNSveTLPdoQuylANnuy6WBOw16XoqH"
   258  		validClient       = "CQ4iZ5sebOfhGRwUn3IV0r1YFMNrMTIx"
   259  		validOrganization = "dc6a6159-3cd5-41a2-b391-553b1351cd98"
   260  	)
   261  	tests := []struct {
   262  		name   string
   263  		config *Credentials
   264  		want   bool
   265  	}{
   266  		{
   267  			name:   "empty credentials",
   268  			config: &Credentials{Client: "", Secret: "", Organization: ""},
   269  			want:   true,
   270  		},
   271  		{
   272  			name:   "empty mandatory credentials - optionals given",
   273  			config: &Credentials{Client: "", Secret: "", Organization: validOrganization},
   274  			want:   true,
   275  		},
   276  		{
   277  			name:   "credentials without id",
   278  			config: &Credentials{Client: "", Secret: validSecret},
   279  			want:   false,
   280  		},
   281  		{
   282  			name:   "credentials without secret",
   283  			config: &Credentials{Client: validClient, Secret: ""},
   284  			want:   false,
   285  		},
   286  		{
   287  			name:   "credentials with all mandatory params set",
   288  			config: &Credentials{Client: validClient, Secret: validSecret},
   289  			want:   false,
   290  		},
   291  	}
   292  
   293  	for _, tt := range tests {
   294  		t.Run(tt.name, func(t *testing.T) {
   295  			got := tt.config.IsEmpty()
   296  			if got != tt.want {
   297  				t.Errorf("Expected %v but got %v, with credentials: %v", tt.want, got, tt.config)
   298  			}
   299  		})
   300  	}
   301  }