Add testing
This commit is contained in:
parent
3233f8c27f
commit
06e6d457da
14 changed files with 273 additions and 5 deletions
|
@ -18,6 +18,8 @@
|
|||
_or_ return an error defined (as public variable) in that package.
|
||||
Example: `other.Error("auth", "failed to do something important", originalError)`
|
||||
- Returned http errors must use `git.mstar.dev/mstar/goutils/webutils.ProblemDetails(StatusOnly)`.
|
||||
- Every function must either have a test or a doc comment explaining
|
||||
why it doesn't have a test
|
||||
|
||||
## JS/TS
|
||||
|
||||
|
|
|
@ -14,6 +14,10 @@ type FSWrapper struct {
|
|||
log bool
|
||||
}
|
||||
|
||||
// Compile time interface implementation assertion
|
||||
var _ fs.FS = &FSWrapper{}
|
||||
|
||||
// No test, as no processing happens, only wrapping the arguments in a struct
|
||||
func NewFSWrapper(wraps fs.FS, appends string, logAccess bool) *FSWrapper {
|
||||
return &FSWrapper{
|
||||
wrapped: wraps,
|
||||
|
@ -22,11 +26,12 @@ func NewFSWrapper(wraps fs.FS, appends string, logAccess bool) *FSWrapper {
|
|||
}
|
||||
}
|
||||
|
||||
func (fs *FSWrapper) Open(name string) (fs.File, error) {
|
||||
res, err := fs.wrapped.Open(fs.toAdd + name)
|
||||
if fs.log {
|
||||
// Pretty sure this can't be reliably tested
|
||||
func (fw *FSWrapper) Open(name string) (fs.File, error) {
|
||||
res, err := fw.wrapped.Open(fw.toAdd + name)
|
||||
if fw.log {
|
||||
log.Debug().
|
||||
Str("prefix", fs.toAdd).
|
||||
Str("prefix", fw.toAdd).
|
||||
Str("filename", name).
|
||||
Err(err).
|
||||
Msg("fswrapper: File access result")
|
||||
|
|
29
shared/fswrapper_test.go
Normal file
29
shared/fswrapper_test.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package shared
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFSWrapper_Open(t *testing.T) {
|
||||
rootFs := os.DirFS("/")
|
||||
wrapper := NewFSWrapper(rootFs, "etc/", false)
|
||||
f, err := wrapper.Open("hostname")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to open /etc/hostname: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
data, err := os.ReadFile("/etc/hostname")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read with full path: %v", err)
|
||||
}
|
||||
wrappedData, err := io.ReadAll(f)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read from wrapped file: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(wrappedData, data) {
|
||||
t.Fatal("file contents are different")
|
||||
}
|
||||
}
|
|
@ -6,6 +6,8 @@ package shared
|
|||
import "golang.org/x/sys/unix"
|
||||
|
||||
// Copied from https://stackoverflow.com/a/20026945 and https://stackoverflow.com/a/49148866
|
||||
// Not testable as host environment could be just about anything
|
||||
// and this function is for testing the host environment
|
||||
func IsWritable(path string) bool {
|
||||
return unix.Access(path, unix.W_OK) == nil
|
||||
}
|
||||
|
|
|
@ -3,6 +3,9 @@ package shared
|
|||
import "os"
|
||||
|
||||
// Copied from https://stackoverflow.com/a/20026945 and https://stackoverflow.com/a/49148866
|
||||
// Not testable as host environment could be just about anything
|
||||
// and this function is for testing the host environment.
|
||||
// Also, Windows is not supported. It may work, it may not, doesn't matter
|
||||
func IsWritable(path string) bool {
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
|
|
108
shared/signing_test.go
Normal file
108
shared/signing_test.go
Normal file
|
@ -0,0 +1,108 @@
|
|||
package shared
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ed25519"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const testStringToSign = "some random text that I made up at work and definitely has no other purpose"
|
||||
|
||||
func TestGenerateKeypairRSA(t *testing.T) {
|
||||
publicBytes, privateBytes, err := GenerateKeypair(false)
|
||||
if err != nil {
|
||||
t.Fatalf("generation failed: %v", err)
|
||||
}
|
||||
publicUntyped, err := x509.ParsePKIXPublicKey(publicBytes)
|
||||
if err != nil {
|
||||
t.Fatalf("parsing public bytes into key failed: %v", err)
|
||||
}
|
||||
public, ok := publicUntyped.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
t.Fatal("asserting public key as rsa.PublicKey failed")
|
||||
}
|
||||
private, err := x509.ParsePKCS1PrivateKey(privateBytes)
|
||||
if err != nil {
|
||||
t.Fatalf("parsing private bytes into key failed: %v", err)
|
||||
}
|
||||
if err := private.Validate(); err != nil {
|
||||
t.Fatalf("validation of private key failed: %v", err)
|
||||
}
|
||||
genPublicRaw := private.Public()
|
||||
genPublic, ok := genPublicRaw.(*rsa.PublicKey)
|
||||
if !reflect.DeepEqual(*public, *genPublic) {
|
||||
t.Fatal("public from generator and from private are different")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateKeypairED(t *testing.T) {
|
||||
publicBytes, privateBytes, err := GenerateKeypair(true)
|
||||
if err != nil {
|
||||
t.Fatalf("generation failed: %v", err)
|
||||
}
|
||||
publicUntyped, err := x509.ParsePKIXPublicKey(publicBytes)
|
||||
if err != nil {
|
||||
t.Fatalf("parsing public bytes into key failed: %v", err)
|
||||
}
|
||||
public, ok := publicUntyped.(ed25519.PublicKey)
|
||||
if !ok {
|
||||
t.Fatal("asserting public key as ed25519.PublicKey failed")
|
||||
}
|
||||
private := ed25519.PrivateKey(privateBytes)
|
||||
pubGenRaw := private.Public()
|
||||
pubGen, ok := pubGenRaw.(ed25519.PublicKey)
|
||||
if !ok {
|
||||
t.Fatal("public key generated by private key is not of type ed25519.PublicKey")
|
||||
}
|
||||
if !reflect.DeepEqual(pubGen, public) {
|
||||
t.Fatal("public from generator and from private are different")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignRSA(t *testing.T) {
|
||||
publicBytes, privateBytes, err := GenerateKeypair(false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate RSA keypair: %v", err)
|
||||
}
|
||||
pubRaw, err := x509.ParsePKIXPublicKey(publicBytes)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse public rsa key: %v", err)
|
||||
}
|
||||
pub, ok := pubRaw.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
t.Fatal("parsed public key is not *rsa.PublicKey")
|
||||
}
|
||||
hash := sha256.Sum256([]byte(testStringToSign))
|
||||
signed, err := Sign(testStringToSign, privateBytes, true)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to sign test string: %v", err)
|
||||
}
|
||||
|
||||
if rsa.VerifyPKCS1v15(pub, crypto.SHA256, hash[:], signed) != nil {
|
||||
t.Fatal("failed to verify")
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyBytesToPem(t *testing.T) {
|
||||
type args struct {
|
||||
bytes []byte
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := KeyBytesToPem(tt.args.bytes, true); got != tt.want {
|
||||
t.Errorf("KeyBytesToPem() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ import (
|
|||
"git.mstar.dev/mstar/goutils/sliceutils"
|
||||
)
|
||||
|
||||
var tagRegex = regexp.MustCompile(`#(\w+)`)
|
||||
var tagRegex = regexp.MustCompile(`#([a-zA-Z0-9\-_]+)`)
|
||||
|
||||
func TagsFromText(text string) []string {
|
||||
matches := tagRegex.FindAllString(text, -1)
|
||||
|
|
34
shared/tagsFromString_test.go
Normal file
34
shared/tagsFromString_test.go
Normal file
|
@ -0,0 +1,34 @@
|
|||
package shared
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTagsFromText(t *testing.T) {
|
||||
type args struct {
|
||||
text string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "No tags",
|
||||
args: args{"Text with no tags whatsoever"},
|
||||
want: []string{},
|
||||
}, {
|
||||
name: "one tag",
|
||||
args: args{"Text with one simple #tag"},
|
||||
want: []string{"tag"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := TagsFromText(tt.args.text); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("TagsFromText() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -4,6 +4,8 @@ import (
|
|||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
// Not testable since it focuses entirely on the side effect of
|
||||
// sending a log out
|
||||
func Trace(l *zerolog.Logger) *zerolog.Logger {
|
||||
if e := l.Trace(); e.Enabled() {
|
||||
e.Caller(2).
|
||||
|
@ -12,6 +14,8 @@ func Trace(l *zerolog.Logger) *zerolog.Logger {
|
|||
return l
|
||||
}
|
||||
|
||||
// Not testable since it focuses entirely on the side effect of
|
||||
// sending a log out
|
||||
func Untrace(l *zerolog.Logger) {
|
||||
if e := l.Trace(); e.Enabled() {
|
||||
e.Caller(2).
|
||||
|
|
|
@ -12,10 +12,16 @@ type ZerologGormAdapter struct {
|
|||
logger zerolog.Logger
|
||||
}
|
||||
|
||||
// Compile time interface implementation enforcement
|
||||
var _ logger.Interface = &ZerologGormAdapter{}
|
||||
|
||||
// Not worth testing as just a wrapper for putting into a struct
|
||||
func NewGormLogger(zerologger zerolog.Logger) *ZerologGormAdapter {
|
||||
return &ZerologGormAdapter{zerologger}
|
||||
}
|
||||
|
||||
// Not testable since it focuses entirely on the side effect of
|
||||
// configuring loglevel
|
||||
func (g *ZerologGormAdapter) LogMode(newLevel logger.LogLevel) logger.Interface {
|
||||
switch newLevel {
|
||||
case logger.Error:
|
||||
|
@ -29,16 +35,23 @@ func (g *ZerologGormAdapter) LogMode(newLevel logger.LogLevel) logger.Interface
|
|||
}
|
||||
return g
|
||||
}
|
||||
|
||||
// Not worth testing since only a wrapper around another function call
|
||||
func (g *ZerologGormAdapter) Info(ctx context.Context, format string, args ...any) {
|
||||
g.logger.Info().Ctx(ctx).Msgf(format, args...)
|
||||
}
|
||||
|
||||
// Not worth testing since only a wrapper around another function call
|
||||
func (g *ZerologGormAdapter) Warn(ctx context.Context, format string, args ...any) {
|
||||
g.logger.Warn().Ctx(ctx).Msgf(format, args...)
|
||||
}
|
||||
|
||||
// Not worth testing since only a wrapper around another function call
|
||||
func (g *ZerologGormAdapter) Error(ctx context.Context, format string, args ...any) {
|
||||
g.logger.Error().Ctx(ctx).Msgf(format, args...)
|
||||
}
|
||||
|
||||
// Not worth testing since only a wrapper around another function call
|
||||
func (g *ZerologGormAdapter) Trace(
|
||||
ctx context.Context,
|
||||
begin time.Time,
|
||||
|
@ -55,10 +68,12 @@ func (g *ZerologGormAdapter) Trace(
|
|||
Send()
|
||||
}
|
||||
|
||||
// Not worth testing since only a wrapper around another function call
|
||||
func (g *ZerologGormAdapter) OverwriteLoggingLevel(new zerolog.Level) {
|
||||
g.logger = g.logger.Level(new)
|
||||
}
|
||||
|
||||
// Not worth testing since only a wrapper around another function call
|
||||
func (g *ZerologGormAdapter) OverwriteLogger(new zerolog.Logger) {
|
||||
g.logger = new
|
||||
}
|
||||
|
|
|
@ -2,6 +2,9 @@ package shared
|
|||
|
||||
import "github.com/rs/zerolog/log"
|
||||
|
||||
// Stuff here is not actually used anymore in the new implementation
|
||||
// thus also no tests needed
|
||||
|
||||
type ZerologWrapper struct{}
|
||||
|
||||
func (z *ZerologWrapper) Errorf(format string, args ...any) {
|
||||
|
|
|
@ -24,14 +24,17 @@ type Note struct {
|
|||
var _ shared.Clonable = &Note{}
|
||||
var _ shared.Sanitisable = &Note{}
|
||||
|
||||
// No test, does nothing currently
|
||||
func (note *Note) Sanitize() {
|
||||
}
|
||||
|
||||
// No test, no data processing, only copy
|
||||
func (note *Note) Clone() shared.Clonable {
|
||||
tmp := *note
|
||||
return &tmp
|
||||
}
|
||||
|
||||
// No test, no data processing, only copy
|
||||
func (n *Note) FromModel(m *models.Note) {
|
||||
n.ID = m.ID
|
||||
n.CreatedAt = m.CreatedAt
|
||||
|
|
|
@ -44,11 +44,13 @@ type User struct {
|
|||
var _ shared.Sanitisable = &User{}
|
||||
var _ shared.Clonable = &User{}
|
||||
|
||||
// No test due to no fancy processing, just setting values to constants
|
||||
func (u *User) Sanitize() {
|
||||
u.Verified = nil
|
||||
u.FinishedRegistration = nil
|
||||
}
|
||||
|
||||
// No test, no data processing, only copy
|
||||
func (u *User) Clone() shared.Clonable {
|
||||
user := *u
|
||||
if u.IconId != nil {
|
||||
|
@ -83,6 +85,7 @@ func (u *User) Clone() shared.Clonable {
|
|||
return &user
|
||||
}
|
||||
|
||||
// No test, no data processing, only copy
|
||||
func (u *User) FromModel(m *models.User) {
|
||||
u.ID = m.ID
|
||||
u.CreatedAt = m.CreatedAt
|
||||
|
|
57
web/shared/client_test.go
Normal file
57
web/shared/client_test.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
package webshared
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const testBody = `{
|
||||
"key": "value",
|
||||
"more-key": 21
|
||||
}`
|
||||
|
||||
var testBodyHash = []byte("22a5173da554010ea25f7d2ae34032a434c3a883a55d65f34c3413fdc555bed3")
|
||||
|
||||
func TestSignRequest(t *testing.T) {
|
||||
// TODO: Implement tests
|
||||
}
|
||||
|
||||
func Test_applyBodyHash_WithBody(t *testing.T) {
|
||||
var headers = make(http.Header, 0)
|
||||
digest := "SHA-256=" + string(testBodyHash)
|
||||
applyBodyHash(headers, []byte(testBody))
|
||||
headerDigest := headers.Get("Digest")
|
||||
if headerDigest != digest {
|
||||
t.Fatalf("digests didn't match: header \"%v\" != precalc \"%v\"", headerDigest, digest)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewRequest(t *testing.T) {
|
||||
type args struct {
|
||||
method string
|
||||
url string
|
||||
body io.Reader
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *http.Request
|
||||
wantErr bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := NewRequest(tt.args.method, tt.args.url, tt.args.body)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("NewRequest() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("NewRequest() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue