github.com/greenpau/go-authcrunch@v1.1.4/pkg/identity/qr/qr_test.go (about)

     1  // Copyright 2022 Paul Greenberg greenpau@outlook.com
     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 qr
    16  
    17  import (
    18  	"fmt"
    19  	"github.com/greenpau/go-authcrunch/internal/tests"
    20  	"testing"
    21  )
    22  
    23  func TestNewCode(t *testing.T) {
    24  	testcases := []struct {
    25  		name      string
    26  		code      *Code
    27  		want      map[string]interface{}
    28  		shouldErr bool
    29  		err       error
    30  	}{
    31  		{
    32  			name: "valid totp app token code with sha1",
    33  			code: &Code{
    34  				Secret:    "foobar",
    35  				Type:      "totp",
    36  				Label:     "app token",
    37  				Period:    30,
    38  				Algorithm: "sha1",
    39  				Issuer:    "Self",
    40  				Digits:    6,
    41  			},
    42  			want: map[string]interface{}{
    43  				"code":    "otpauth://totp/app+token?secret=MZXW6YTBOI&issuer=Self&algorithm=sha1&digits=6&period=30",
    44  				"encoded": "b3RwYXV0aDovL3RvdHAvYXBwK3Rva2VuP3NlY3JldD1NWlhXNllUQk9JJmlzc3Vlcj1TZWxmJmFsZ29yaXRobT1zaGExJmRpZ2l0cz02JnBlcmlvZD0zMA==",
    45  			},
    46  		},
    47  		{
    48  			name: "valid hotp app token code with sha1",
    49  			code: &Code{
    50  				Secret:    "foobar",
    51  				Type:      "hotp",
    52  				Label:     "app token",
    53  				Algorithm: "sha1",
    54  				Issuer:    "Self",
    55  				Counter:   100,
    56  			},
    57  			want: map[string]interface{}{
    58  				"code":    "otpauth://hotp/app+token?secret=MZXW6YTBOI&issuer=Self&algorithm=sha1&digits=6&counter=100&period=30",
    59  				"encoded": "b3RwYXV0aDovL2hvdHAvYXBwK3Rva2VuP3NlY3JldD1NWlhXNllUQk9JJmlzc3Vlcj1TZWxmJmFsZ29yaXRobT1zaGExJmRpZ2l0cz02JmNvdW50ZXI9MTAwJnBlcmlvZD0zMA==",
    60  			},
    61  		},
    62  		{
    63  			name: "valid totp app token code with defaults",
    64  			code: &Code{
    65  				Secret: "foobar",
    66  				Type:   "totp",
    67  				Label:  "app token",
    68  				Issuer: "Self",
    69  			},
    70  			want: map[string]interface{}{
    71  				"code":    "otpauth://totp/app+token?secret=MZXW6YTBOI&issuer=Self&digits=6&period=30",
    72  				"encoded": "b3RwYXV0aDovL3RvdHAvYXBwK3Rva2VuP3NlY3JldD1NWlhXNllUQk9JJmlzc3Vlcj1TZWxmJmRpZ2l0cz02JnBlcmlvZD0zMA==",
    73  			},
    74  		},
    75  		{
    76  			name: "invalid token code without type",
    77  			code: &Code{
    78  				Label:  "app token",
    79  				Secret: "foobar",
    80  			},
    81  			shouldErr: true,
    82  			err:       fmt.Errorf("token type must be either totp or hotp"),
    83  		},
    84  		{
    85  			name: "invalid token code without label",
    86  			code: &Code{
    87  				Type: "totp",
    88  			},
    89  			shouldErr: true,
    90  			err:       fmt.Errorf("token label must be set"),
    91  		},
    92  		{
    93  			name: "invalid token code without secret",
    94  			code: &Code{
    95  				Type:  "totp",
    96  				Label: "app token",
    97  			},
    98  			shouldErr: true,
    99  			err:       fmt.Errorf("token secret must be set"),
   100  		},
   101  		{
   102  			name: "invalid token code with secret too short",
   103  			code: &Code{
   104  				Type:   "totp",
   105  				Label:  "app token",
   106  				Secret: "12345",
   107  			},
   108  			shouldErr: true,
   109  			err:       fmt.Errorf("token secret must be at least 6 characters long"),
   110  		},
   111  		{
   112  			name: "invalid token code with invalid digits value",
   113  			code: &Code{
   114  				Type:   "totp",
   115  				Label:  "app token",
   116  				Secret: "foobar",
   117  				Digits: 100,
   118  			},
   119  			shouldErr: true,
   120  			err:       fmt.Errorf("digits must be between 4 and 8 numbers long"),
   121  		},
   122  		{
   123  			name: "invalid token code with invalid period value",
   124  			code: &Code{
   125  				Type:   "totp",
   126  				Label:  "app token",
   127  				Secret: "foobar",
   128  				Period: 360,
   129  			},
   130  			shouldErr: true,
   131  			err:       fmt.Errorf("token period must be between 30 and 180 seconds"),
   132  		},
   133  		{
   134  			name: "invalid hotp token code without counter",
   135  			code: &Code{
   136  				Type:   "hotp",
   137  				Label:  "app token",
   138  				Secret: "foobar",
   139  			},
   140  			shouldErr: true,
   141  			err:       fmt.Errorf("hotp token counter must be set"),
   142  		},
   143  		{
   144  			name: "invalid token code with invalid algorithm value",
   145  			code: &Code{
   146  				Type:      "totp",
   147  				Label:     "app token",
   148  				Secret:    "foobar",
   149  				Algorithm: "foobar",
   150  			},
   151  			shouldErr: true,
   152  			err:       fmt.Errorf("token algo must be SHA1, SHA256, or SHA512"),
   153  		},
   154  		{
   155  			name:      "empty code",
   156  			shouldErr: true,
   157  			err:       fmt.Errorf("token label must be set"),
   158  		},
   159  	}
   160  
   161  	for _, tc := range testcases {
   162  		t.Run(tc.name, func(t *testing.T) {
   163  			msgs := []string{fmt.Sprintf("test name: %s", tc.name)}
   164  			if tc.code == nil {
   165  				tc.code = NewCode()
   166  			}
   167  			err := tc.code.Build()
   168  			if tests.EvalErrWithLog(t, err, "", tc.shouldErr, tc.err, msgs) {
   169  				return
   170  			}
   171  			got := map[string]interface{}{
   172  				"code":    tc.code.Get(),
   173  				"encoded": tc.code.GetEncoded(),
   174  			}
   175  			tests.EvalObjectsWithLog(t, "qr code", tc.want, got, msgs)
   176  		})
   177  	}
   178  }