From 678d8baaea63cf7b59f67df1ee5cd1d03da736ca Mon Sep 17 00:00:00 2001 From: nanjiangshu Date: Tue, 3 Sep 2024 11:22:18 +0200 Subject: [PATCH] fix unit tests for helpers --- sda-admin/helpers/helpers.go | 6 +- sda-admin/helpers/helpers_test.go | 435 ++++-------------------------- 2 files changed, 59 insertions(+), 382 deletions(-) diff --git a/sda-admin/helpers/helpers.go b/sda-admin/helpers/helpers.go index 56e14c1fc..c98f07930 100644 --- a/sda-admin/helpers/helpers.go +++ b/sda-admin/helpers/helpers.go @@ -54,18 +54,18 @@ func CheckTokenExpiration(accessToken string) error { switch untilExp := time.Until(expiration); { case untilExp < 0: - return fmt.Errorf("# the provided access token has expired, please renew it") + return fmt.Errorf("the provided access token has expired, please renew it") case untilExp > 0 && untilExp < 24*time.Hour: fmt.Fprintln( os.Stderr, - "# The provided access token expires in", + "The provided access token expires in", time.Until(expiration).Truncate(time.Second), ) fmt.Fprintln(os.Stderr, "Consider renewing the token.") default: fmt.Fprintln( os.Stderr, - "# The provided access token expires in", + "The provided access token expires in", time.Until(expiration).Truncate(time.Second), ) } diff --git a/sda-admin/helpers/helpers_test.go b/sda-admin/helpers/helpers_test.go index 56d8e75fb..fa059aa74 100644 --- a/sda-admin/helpers/helpers_test.go +++ b/sda-admin/helpers/helpers_test.go @@ -3,25 +3,16 @@ package helpers import ( "crypto/rand" "crypto/rsa" - "fmt" "log" - "os" - "runtime" - "strings" + "net/http" + "net/http/httptest" "testing" "time" "github.com/golang-jwt/jwt" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" ) -type HelperTests struct { - suite.Suite - tempDir string - testFile *os.File -} - // generate jwts for testing the expDate func generateDummyToken(expDate int64) string { // Generate a new private key @@ -50,383 +41,69 @@ func generateDummyToken(expDate int64) string { return ss } -func TestHelpersTestSuite(t *testing.T) { - suite.Run(t, new(HelperTests)) -} - -func (suite *HelperTests) SetupTest() { - - var err error - - // Create a temporary directory for our files - suite.tempDir, err = os.MkdirTemp(os.TempDir(), "sda-cli-test-") - if err != nil { - log.Fatal("Couldn't create temporary test directory", err) - } - - // create an existing test file with some known content - suite.testFile, err = os.CreateTemp(suite.tempDir, "testfile-") - if err != nil { - log.Fatal("cannot create temporary public key file", err) - } - - err = os.WriteFile(suite.testFile.Name(), []byte("content"), 0600) - if err != nil { - log.Fatalf("failed to write to testfile: %s", err) - } -} - -func (suite *HelperTests) TearDownTest() { - os.Remove(suite.testFile.Name()) - os.Remove(suite.tempDir) -} - -func (suite *HelperTests) TestFileExists() { - // file exists - testExists := FileExists(suite.testFile.Name()) - suite.Equal(testExists, true) - // file does not exists - testMissing := FileExists("does-not-exist") - suite.Equal(testMissing, false) - // file is a directory - testIsDir := FileExists(suite.tempDir) - suite.Equal(testIsDir, true) -} - -func (suite *HelperTests) TestFileIsReadable() { - // file doesn't exist - testMissing := FileIsReadable("does-not-exist") - suite.Equal(testMissing, false) - - // file is a directory - testIsDir := FileIsReadable(suite.tempDir) - suite.Equal(testIsDir, false) - - // file can be read - testFileOk := FileIsReadable(suite.testFile.Name()) - suite.Equal(testFileOk, true) - - // test file permissions. This doesn't work on windows, so we do an extra - // check to see if this test makes sense. - if runtime.GOOS != "windows" { - err := os.Chmod(suite.testFile.Name(), 0000) - if err != nil { - log.Fatal("Couldn't set file permissions of test file") - } - // file permissions don't allow reading - testDisallowed := FileIsReadable(suite.testFile.Name()) - suite.Equal(testDisallowed, false) - - // restore permissions - err = os.Chmod(suite.testFile.Name(), 0600) - if err != nil { - log.Fatal("Couldn't restore file permissions of test file") - } - } -} - -func (suite *HelperTests) TestFormatSubcommandUsage() { - // check formatting of malformed usage strings without %s for os.Args[0] - malformedNoFormatString := "USAGE: do that stuff" - testMissingArgsFormat := FormatSubcommandUsage(malformedNoFormatString) - suite.Equal(malformedNoFormatString, testMissingArgsFormat) - - // check formatting when the USAGE string is missing - malformedNoUsage := `module: this module does all the fancies stuff, - and virtually none of the non-fancy stuff. - run with: %s module` - testNoUsage := FormatSubcommandUsage(malformedNoUsage) - suite.Equal(fmt.Sprintf(malformedNoUsage, os.Args[0]), testNoUsage) - - // check formatting when the usage string is correctly formatted - - correctUsage := `USAGE: %s module - -module: - this module does all the fancies stuff, - and virtually none of the non-fancy stuff.` - - correctFormat := fmt.Sprintf(` -module: - this module does all the fancies stuff, - and virtually none of the non-fancy stuff. - - USAGE: %s module - -`, os.Args[0]) - testCorrect := FormatSubcommandUsage(correctUsage) - suite.Equal(correctFormat, testCorrect) - -} - -func (suite *HelperTests) TestParseS3ErrorResponse() { - // check bad response body by creating and passing - // a dummy faulty io.Reader - f, _ := os.Open(`doesn't exist`) - defer f.Close() - msg, err := ParseS3ErrorResponse(f) - suite.Equal("", msg) - suite.ErrorContains(err, "failed to read from response body") - - // check not xml - payload := strings.NewReader("some non xml text") - msg, err = ParseS3ErrorResponse(payload) - suite.Equal("", msg) - suite.EqualError(err, "cannot parse response body, reason: not xml") - - // check with malformed xml - payload.Reset("\nAll access to this bucket has been disabled./minio/test/dummy/data_file1.c4gh73e4c710-46e8-4846-b70b-86ee905a3ab0") - msg, err = ParseS3ErrorResponse(payload) - suite.Equal("", msg) - suite.ErrorContains(err, "failed to unmarshal xml response") - - // check with good xml - payload.Reset("\nAllAccessDisabledAll access to this bucket has been disabled./minio/test/dummy/data_file1.c4gh73e4c710-46e8-4846-b70b-86ee905a3ab0") - msg, err = ParseS3ErrorResponse(payload) - suite.Equal("{Code:AllAccessDisabled Message:All access to this bucket has been disabled. Resource:/minio/test/dummy/data_file1.c4gh}", msg) - suite.NoError(err) -} - -func (suite *HelperTests) TestConfigNoFile() { - msg := "open nofile.conf: no such file or directory" - if runtime.GOOS == "windows" { - msg = "open nofile.conf: The system cannot find the file specified." - } - configPath := "nofile.conf" - - _, err := LoadConfigFile(configPath) - assert.EqualError(suite.T(), err, msg) -} - -func (suite *HelperTests) TestConfigWrongFile() { - var confFile = ` -access_token = someToken -access_key = someUser -host_bucket = someHostBase -guess_mime_type!True -encrypt = False -` - - configPath, err := os.CreateTemp(os.TempDir(), "s3cmd-") - if err != nil { - log.Fatal(err) - } - - defer os.Remove(configPath.Name()) - - err = os.WriteFile(configPath.Name(), []byte(confFile), 0600) - if err != nil { - log.Printf("failed to write temp config file, %v", err) - } - - _, err = LoadConfigFile(configPath.Name()) - assert.EqualError(suite.T(), err, "key-value delimiter not found: guess_mime_type!True\n") -} - -func (suite *HelperTests) TestConfigS3cmdFileFormat() { - var confFile = ` - [some header] - access_token = someToken - host_base = someHostBase - host_bucket = someHostBase - secret_key = someUser - access_key = someUser -` - - configPath, err := os.CreateTemp(os.TempDir(), "s3cmd-") - if err != nil { - log.Fatal(err) - } - - defer os.Remove(configPath.Name()) - - err = os.WriteFile(configPath.Name(), []byte(confFile), 0600) - if err != nil { - log.Printf("failed to write temp config file, %v", err) - } - - _, err = LoadConfigFile(configPath.Name()) - assert.NoError(suite.T(), err) -} - -func (suite *HelperTests) TestConfigMissingCredentials() { - - configPath, err := os.CreateTemp(os.TempDir(), "s3cmd-") - if err != nil { - log.Fatal(err) - } - - defer os.Remove(configPath.Name()) - - _, err = LoadConfigFile(configPath.Name()) - assert.EqualError(suite.T(), err, "failed to find credentials in configuration file") -} - -func (suite *HelperTests) TestConfigMissingEndpoint() { - var confFile = ` -access_token = someToken -access_key = someUser -` - configPath, err := os.CreateTemp(os.TempDir(), "s3cmd-") - if err != nil { - log.Fatal(err) - } - - defer os.Remove(configPath.Name()) - - if err := os.WriteFile(configPath.Name(), []byte(confFile), 0600); err != nil { - log.Printf("failed to write temp config file, %v", err) - } - - _, err = LoadConfigFile(configPath.Name()) - assert.EqualError(suite.T(), err, "failed to find endpoint in configuration file") -} - -func (suite *HelperTests) TestConfig() { - var confFile = ` -access_token = someToken -host_base = someHostBase -encoding = UTF-8 -host_bucket = someHostBase -multipart_chunk_size_mb = 50 -secret_key = someUser -access_key = someUser -use_https = True -check_ssl_certificate = False -check_ssl_hostname = False -socket_timeout = 30 -human_readable_sizes = True -guess_mime_type = True -encrypt = False -` - configPath, err := os.CreateTemp(os.TempDir(), "s3cmd-") - if err != nil { - log.Fatal(err) - } - - defer os.Remove(configPath.Name()) - - err = os.WriteFile(configPath.Name(), []byte(confFile), 0600) - if err != nil { - log.Printf("failed to write temp config file, %v", err) - } - - _, err = LoadConfigFile(configPath.Name()) - assert.NoError(suite.T(), err) -} - -func (suite *HelperTests) TestTokenExpiration() { +func TestTokenExpiration(t *testing.T) { // Token without exp claim token := generateDummyToken(0) err := CheckTokenExpiration(token) - assert.EqualError(suite.T(), err, "could not parse token, reason: no expiration date") + assert.EqualError(t, err, "could not parse token, reason: no expiration date") // Token with expired date token = generateDummyToken(time.Now().Unix()) err = CheckTokenExpiration(token) - assert.EqualError(suite.T(), err, "the provided access token has expired, please renew it") + assert.EqualError(t, err, "the provided access token has expired, please renew it") // Token with valid expiration token = generateDummyToken(time.Now().Add(time.Hour * 72).Unix()) err = CheckTokenExpiration(token) - assert.NoError(suite.T(), err) -} - -func (suite *HelperTests) TestPubKeyEmptyField() { - var confFile = ` -access_token = someToken -host_base = someHostBase -encoding = UTF-8 -host_bucket = someHostBase -multipart_chunk_size_mb = 50 -secret_key = someUser -access_key = someUser -use_https = True -check_ssl_certificate = False -check_ssl_hostname = False -socket_timeout = 30 -human_readable_sizes = True -guess_mime_type = True -encrypt = False -` - configPath, err := os.Create(".sda-cli-session") - if err != nil { - log.Fatal(err) - } - - defer os.Remove(configPath.Name()) - - err = os.WriteFile(configPath.Name(), []byte(confFile), 0600) - if err != nil { - log.Printf("failed to write temp config file, %v", err) - } - - _, err = GetPublicKeyFromSession() - assert.EqualError(suite.T(), err, "public key not found in the configuration") -} - -func (suite *HelperTests) TestGetPublicKeyFromSession() { - - var confFile = ` -access_token = someToken -host_base = someHostBase -encoding = UTF-8 -host_bucket = someHostBase -multipart_chunk_size_mb = 50 -secret_key = someUser -access_key = someUser -use_https = True -check_ssl_certificate = False -check_ssl_hostname = False -socket_timeout = 30 -human_readable_sizes = True -guess_mime_type = True -encrypt = False -public_key = 27be42445fd9e39c9be39e6b36a55e61e3801fc845f63781a813d3fe9977e17a -` - configPath, err := os.Create(".sda-cli-session") - if err != nil { - log.Fatal(err) - } - - defer os.Remove(configPath.Name()) - - err = os.WriteFile(configPath.Name(), []byte(confFile), 0600) - if err != nil { - log.Printf("failed to write temp config file, %v", err) - } - - _, err = GetPublicKeyFromSession() - assert.NoError(suite.T(), err) - - if assert.FileExists(suite.T(), "key-from-oidc.pub.pem") { - os.Remove("key-from-oidc.pub.pem") - } -} - -func (suite *HelperTests) TestInvalidCharacters() { - // Test that file paths with invalid characters trigger errors - for _, badc := range "\x00\x7F\x1A:*?\\<>\"|!'();@&=+$,%#[]" { - badchar := string(badc) - testfilepath := "test" + badchar + "file" - - err := CheckValidChars(testfilepath) - assert.Error(suite.T(), err) - assert.Equal(suite.T(), fmt.Sprintf("filepath %v contains disallowed characters: %+v", testfilepath, badchar), err.Error()) - } -} - -func (suite *HelperTests) TestCreatePubFile() { - var pubKeyContent = `339eb2a458fec5e23aa8b57cfcb35f10e7389025816e44d4234f814ed2aeed3f` - var expectedPubKey = `-----BEGIN CRYPT4GH PUBLIC KEY----- -MzM5ZWIyYTQ1OGZlYzVlMjNhYThiNTdjZmNiMzVmMTA= ------END CRYPT4GH PUBLIC KEY----- -` - _, err := CreatePubFile(pubKeyContent, os.TempDir()+"/test_public_file.pub.pem") - assert.NoError(suite.T(), err) - - pubFile, _ := os.ReadFile(os.TempDir() + "/test_public_file.pub.pem") - s := string(pubFile) - assert.Equal(suite.T(), expectedPubKey, s) - defer os.Remove(os.TempDir() + "/test_public_file.pub.pem") + assert.NoError(t, err) +} + +func TestGetBody(t *testing.T) { + // Mock server + mockResponse := `{"key": "value"}` + server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Write([]byte(mockResponse)) + })) + defer server.Close() + + // Test successful case + body, err := GetBody(server.URL, "mock_token") + assert.NoError(t, err) + assert.JSONEq(t, mockResponse, string(body)) + + // Test non-200 status code + serverError := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.WriteHeader(http.StatusInternalServerError) + })) + defer serverError.Close() + + body, err = GetBody(serverError.URL, "mock_token") + assert.Error(t, err) + assert.Nil(t, body) +} + +func TestPostReq(t *testing.T) { + // Mock server + mockResponse := `{"key": "value"}` + server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + assert.Equal(t, "application/json", req.Header.Get("Content-Type")) + assert.Equal(t, "Bearer mock_token", req.Header.Get("Authorization")) + rw.Write([]byte(mockResponse)) + })) + defer server.Close() + + // Test successful case + body, err := PostReq(server.URL, "mock_token", []byte(`{"name":"test"}`)) + assert.NoError(t, err) + assert.JSONEq(t, mockResponse, string(body)) + + // Test non-200 status code + serverError := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.WriteHeader(http.StatusInternalServerError) + })) + defer serverError.Close() + + body, err = PostReq(serverError.URL, "mock_token", []byte(`{"name":"test"}`)) + assert.Error(t, err) + assert.Nil(t, body) }