mirror of
https://github.com/Maronato/go-finger.git
synced 2025-03-15 00:34:47 +00:00
445 lines
10 KiB
Go
445 lines
10 KiB
Go
|
package webfinger_test
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"encoding/json"
|
||
|
"os"
|
||
|
"reflect"
|
||
|
"sort"
|
||
|
"strings"
|
||
|
"testing"
|
||
|
|
||
|
"git.maronato.dev/maronato/finger/internal/config"
|
||
|
"git.maronato.dev/maronato/finger/internal/log"
|
||
|
"git.maronato.dev/maronato/finger/internal/webfinger"
|
||
|
)
|
||
|
|
||
|
func newTempFile(t *testing.T, content string) (name string, remove func()) {
|
||
|
t.Helper()
|
||
|
|
||
|
f, err := os.CreateTemp("", "finger-test")
|
||
|
if err != nil {
|
||
|
t.Fatalf("error creating temp file: %v", err)
|
||
|
}
|
||
|
|
||
|
_, err = f.WriteString(content)
|
||
|
if err != nil {
|
||
|
t.Fatalf("error writing to temp file: %v", err)
|
||
|
}
|
||
|
|
||
|
return f.Name(), func() {
|
||
|
err = os.Remove(f.Name())
|
||
|
if err != nil {
|
||
|
t.Fatalf("error removing temp file: %v", err)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestNewFingerReader(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
|
||
|
f := webfinger.NewFingerReader()
|
||
|
|
||
|
if f == nil {
|
||
|
t.Errorf("NewFingerReader() = %v, want: %v", f, nil)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestFingerReader_ReadFiles(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
|
||
|
tests := []struct {
|
||
|
name string
|
||
|
urnsContent string
|
||
|
fingersContent string
|
||
|
useURNFile bool
|
||
|
useFingerFile bool
|
||
|
wantErr bool
|
||
|
}{
|
||
|
{
|
||
|
name: "reads files",
|
||
|
urnsContent: "name: https://schema/name\nprofile: https://schema/profile",
|
||
|
fingersContent: "user@example.com:\n name: John Doe",
|
||
|
useURNFile: true,
|
||
|
useFingerFile: true,
|
||
|
wantErr: false,
|
||
|
},
|
||
|
{
|
||
|
name: "errors on missing URNs file",
|
||
|
urnsContent: "invalid",
|
||
|
fingersContent: "user@example.com:\n name: John Doe",
|
||
|
useURNFile: false,
|
||
|
useFingerFile: true,
|
||
|
wantErr: true,
|
||
|
},
|
||
|
{
|
||
|
name: "errors on missing fingers file",
|
||
|
urnsContent: "name: https://schema/name\nprofile: https://schema/profile",
|
||
|
fingersContent: "invalid",
|
||
|
useFingerFile: false,
|
||
|
useURNFile: true,
|
||
|
wantErr: true,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for _, tt := range tests {
|
||
|
tc := tt
|
||
|
|
||
|
t.Run(tc.name, func(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
|
||
|
cfg := config.NewConfig()
|
||
|
|
||
|
urnsFileName, urnsCleanup := newTempFile(t, tc.urnsContent)
|
||
|
defer urnsCleanup()
|
||
|
|
||
|
fingersFileName, fingersCleanup := newTempFile(t, tc.fingersContent)
|
||
|
defer fingersCleanup()
|
||
|
|
||
|
if !tc.useURNFile {
|
||
|
cfg.URNPath = "invalid"
|
||
|
} else {
|
||
|
cfg.URNPath = urnsFileName
|
||
|
}
|
||
|
|
||
|
if !tc.useFingerFile {
|
||
|
cfg.FingerPath = "invalid"
|
||
|
} else {
|
||
|
cfg.FingerPath = fingersFileName
|
||
|
}
|
||
|
|
||
|
f := webfinger.NewFingerReader()
|
||
|
|
||
|
err := f.ReadFiles(cfg)
|
||
|
if err != nil {
|
||
|
if !tc.wantErr {
|
||
|
t.Errorf("ReadFiles() error = %v", err)
|
||
|
}
|
||
|
|
||
|
return
|
||
|
} else if tc.wantErr {
|
||
|
t.Errorf("ReadFiles() error = %v, wantErr %v", err, tc.wantErr)
|
||
|
}
|
||
|
|
||
|
if !reflect.DeepEqual(f.URNSFile, []byte(tc.urnsContent)) {
|
||
|
t.Errorf("ReadFiles() URNsFile = %v, want: %v", f.URNSFile, tc.urnsContent)
|
||
|
}
|
||
|
|
||
|
if !reflect.DeepEqual(f.FingersFile, []byte(tc.fingersContent)) {
|
||
|
t.Errorf("ReadFiles() FingersFile = %v, want: %v", f.FingersFile, tc.fingersContent)
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestParseFingers(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
|
||
|
tests := []struct {
|
||
|
name string
|
||
|
rawFingers webfinger.RawFingersMap
|
||
|
want webfinger.WebFingers
|
||
|
wantErr bool
|
||
|
}{
|
||
|
{
|
||
|
name: "parses links",
|
||
|
rawFingers: webfinger.RawFingersMap{
|
||
|
"user@example.com": {
|
||
|
"profile": "https://example.com/profile",
|
||
|
"invalidalias": "https://example.com/invalidalias",
|
||
|
"https://something": "https://somethingelse",
|
||
|
},
|
||
|
},
|
||
|
want: webfinger.WebFingers{
|
||
|
"acct:user@example.com": {
|
||
|
Subject: "acct:user@example.com",
|
||
|
Links: []webfinger.Link{
|
||
|
{
|
||
|
Rel: "https://schema/profile",
|
||
|
Href: "https://example.com/profile",
|
||
|
},
|
||
|
{
|
||
|
Rel: "invalidalias",
|
||
|
Href: "https://example.com/invalidalias",
|
||
|
},
|
||
|
{
|
||
|
Rel: "https://something",
|
||
|
Href: "https://somethingelse",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
wantErr: false,
|
||
|
},
|
||
|
{
|
||
|
name: "parses properties",
|
||
|
rawFingers: webfinger.RawFingersMap{
|
||
|
"user@example.com": {
|
||
|
"name": "John Doe",
|
||
|
"invalidalias": "value1",
|
||
|
"https://mylink": "value2",
|
||
|
},
|
||
|
},
|
||
|
want: webfinger.WebFingers{
|
||
|
"acct:user@example.com": {
|
||
|
Subject: "acct:user@example.com",
|
||
|
Properties: map[string]string{
|
||
|
"https://schema/name": "John Doe",
|
||
|
"invalidalias": "value1",
|
||
|
"https://mylink": "value2",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
wantErr: false,
|
||
|
},
|
||
|
{
|
||
|
name: "accepts acct: prefix",
|
||
|
rawFingers: webfinger.RawFingersMap{
|
||
|
"acct:user@example.com": {
|
||
|
"name": "John Doe",
|
||
|
},
|
||
|
},
|
||
|
want: webfinger.WebFingers{
|
||
|
"acct:user@example.com": {
|
||
|
Subject: "acct:user@example.com",
|
||
|
Properties: map[string]string{
|
||
|
"https://schema/name": "John Doe",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
wantErr: false,
|
||
|
},
|
||
|
{
|
||
|
name: "accepts urls as resource",
|
||
|
rawFingers: webfinger.RawFingersMap{
|
||
|
"https://example.com": {
|
||
|
"name": "John Doe",
|
||
|
},
|
||
|
},
|
||
|
want: webfinger.WebFingers{
|
||
|
"https://example.com": {
|
||
|
Subject: "https://example.com",
|
||
|
Properties: map[string]string{
|
||
|
"https://schema/name": "John Doe",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
wantErr: false,
|
||
|
},
|
||
|
{
|
||
|
name: "accepts multiple resources",
|
||
|
rawFingers: webfinger.RawFingersMap{
|
||
|
"user@example.com": {
|
||
|
"name": "John Doe",
|
||
|
},
|
||
|
"other@example.com": {
|
||
|
"name": "Jane Doe",
|
||
|
},
|
||
|
},
|
||
|
want: webfinger.WebFingers{
|
||
|
"acct:user@example.com": {
|
||
|
Subject: "acct:user@example.com",
|
||
|
Properties: map[string]string{
|
||
|
"https://schema/name": "John Doe",
|
||
|
},
|
||
|
},
|
||
|
"acct:other@example.com": {
|
||
|
Subject: "acct:other@example.com",
|
||
|
Properties: map[string]string{
|
||
|
"https://schema/name": "Jane Doe",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
wantErr: false,
|
||
|
},
|
||
|
{
|
||
|
name: "errors on invalid resource",
|
||
|
rawFingers: webfinger.RawFingersMap{
|
||
|
"invalid": {
|
||
|
"name": "John Doe",
|
||
|
},
|
||
|
},
|
||
|
want: nil,
|
||
|
wantErr: true,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for _, tt := range tests {
|
||
|
tc := tt
|
||
|
|
||
|
t.Run(tc.name, func(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
|
||
|
// Create a urn map
|
||
|
urns := webfinger.URNMap{
|
||
|
"name": "https://schema/name",
|
||
|
"profile": "https://schema/profile",
|
||
|
}
|
||
|
|
||
|
ctx := context.Background()
|
||
|
cfg := config.NewConfig()
|
||
|
l := log.NewLogger(&strings.Builder{}, cfg)
|
||
|
|
||
|
ctx = log.WithLogger(ctx, l)
|
||
|
|
||
|
f := webfinger.NewFingerReader()
|
||
|
|
||
|
got, err := f.ParseFingers(ctx, urns, tc.rawFingers)
|
||
|
if (err != nil) != tc.wantErr {
|
||
|
t.Errorf("ParseFingers() error = %v, wantErr %v", err, tc.wantErr)
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Sort links to make it easier to compare
|
||
|
for _, v := range got {
|
||
|
for range v.Links {
|
||
|
sort.Slice(v.Links, func(i, j int) bool {
|
||
|
return v.Links[i].Rel < v.Links[j].Rel
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for _, v := range tc.want {
|
||
|
for range v.Links {
|
||
|
sort.Slice(v.Links, func(i, j int) bool {
|
||
|
return v.Links[i].Rel < v.Links[j].Rel
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if !reflect.DeepEqual(got, tc.want) {
|
||
|
// Unmarshal the structs to JSON to make it easier to print
|
||
|
gotstr := &strings.Builder{}
|
||
|
gotenc := json.NewEncoder(gotstr)
|
||
|
|
||
|
wantstr := &strings.Builder{}
|
||
|
wantenc := json.NewEncoder(wantstr)
|
||
|
|
||
|
_ = gotenc.Encode(got)
|
||
|
_ = wantenc.Encode(tc.want)
|
||
|
|
||
|
t.Errorf("ParseFingers() got = \n%s want: \n%s", gotstr.String(), wantstr.String())
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestReadFingerFile(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
|
||
|
tests := []struct {
|
||
|
name string
|
||
|
urnsContent string
|
||
|
fingersContent string
|
||
|
wantURN webfinger.URNMap
|
||
|
wantFinger webfinger.RawFingersMap
|
||
|
returns *webfinger.WebFingers
|
||
|
wantErr bool
|
||
|
}{
|
||
|
{
|
||
|
name: "reads files",
|
||
|
urnsContent: "name: https://schema/name\nprofile: https://schema/profile",
|
||
|
fingersContent: "user@example.com:\n name: John Doe",
|
||
|
wantURN: webfinger.URNMap{
|
||
|
"name": "https://schema/name",
|
||
|
"profile": "https://schema/profile",
|
||
|
},
|
||
|
wantFinger: webfinger.RawFingersMap{
|
||
|
"user@example.com": {
|
||
|
"name": "John Doe",
|
||
|
},
|
||
|
},
|
||
|
returns: &webfinger.WebFingers{
|
||
|
"acct:user@example.com": {
|
||
|
Subject: "acct:user@example.com",
|
||
|
Properties: map[string]string{
|
||
|
"https://schema/name": "John Doe",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
wantErr: false,
|
||
|
},
|
||
|
{
|
||
|
name: "uses custom URNs",
|
||
|
urnsContent: "favorite_food: https://schema/favorite_food",
|
||
|
fingersContent: "user@example.com:\n favorite_food: Apple",
|
||
|
wantURN: webfinger.URNMap{
|
||
|
"favorite_food": "https://schema/favorite_food",
|
||
|
},
|
||
|
wantFinger: webfinger.RawFingersMap{
|
||
|
"user@example.com": {
|
||
|
"https://schema/favorite_food": "Apple",
|
||
|
},
|
||
|
},
|
||
|
wantErr: false,
|
||
|
},
|
||
|
{
|
||
|
name: "errors on invalid URNs file",
|
||
|
urnsContent: "invalid",
|
||
|
fingersContent: "user@example.com:\n name: John Doe",
|
||
|
wantURN: webfinger.URNMap{},
|
||
|
wantFinger: webfinger.RawFingersMap{},
|
||
|
wantErr: true,
|
||
|
},
|
||
|
{
|
||
|
name: "errors on invalid fingers file",
|
||
|
urnsContent: "name: https://schema/name\nprofile: https://schema/profile",
|
||
|
fingersContent: "invalid",
|
||
|
wantURN: webfinger.URNMap{},
|
||
|
wantFinger: webfinger.RawFingersMap{},
|
||
|
wantErr: true,
|
||
|
},
|
||
|
{
|
||
|
name: "errors on invalid URNs values",
|
||
|
urnsContent: "name: invalid",
|
||
|
fingersContent: "user@example.com:\n name: John Doe",
|
||
|
wantURN: webfinger.URNMap{},
|
||
|
wantFinger: webfinger.RawFingersMap{},
|
||
|
wantErr: true,
|
||
|
},
|
||
|
{
|
||
|
name: "errors on invalid fingers values",
|
||
|
urnsContent: "name: https://schema/name\nprofile: https://schema/profile",
|
||
|
fingersContent: "invalid:\n name: John Doe",
|
||
|
wantURN: webfinger.URNMap{},
|
||
|
wantFinger: webfinger.RawFingersMap{},
|
||
|
wantErr: true,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for _, tt := range tests {
|
||
|
tc := tt
|
||
|
|
||
|
t.Run(tc.name, func(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
|
||
|
ctx := context.Background()
|
||
|
cfg := config.NewConfig()
|
||
|
l := log.NewLogger(&strings.Builder{}, cfg)
|
||
|
|
||
|
ctx = log.WithLogger(ctx, l)
|
||
|
|
||
|
f := webfinger.NewFingerReader()
|
||
|
|
||
|
f.FingersFile = []byte(tc.fingersContent)
|
||
|
f.URNSFile = []byte(tc.urnsContent)
|
||
|
|
||
|
got, err := f.ReadFingerFile(ctx)
|
||
|
if err != nil {
|
||
|
if !tc.wantErr {
|
||
|
t.Errorf("ReadFingerFile() error = %v", err)
|
||
|
}
|
||
|
|
||
|
return
|
||
|
} else if tc.wantErr {
|
||
|
t.Errorf("ReadFingerFile() error = %v, wantErr %v", err, tc.wantErr)
|
||
|
}
|
||
|
|
||
|
if tc.returns != nil && !reflect.DeepEqual(got, *tc.returns) {
|
||
|
t.Errorf("ReadFingerFile() got = %v, want: %v", got, *tc.returns)
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
}
|