Skip to content

Commit

Permalink
Merge pull request #24 from mschoch/add-transport-tests
Browse files Browse the repository at this point in the history
add transport tests
  • Loading branch information
mschoch authored Jun 12, 2024
2 parents 4fb2eb3 + 2f037d0 commit 652d9f9
Show file tree
Hide file tree
Showing 3 changed files with 327 additions and 7 deletions.
37 changes: 30 additions & 7 deletions meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,28 @@ package main
import "strings"

const fireproofPrefix = "fp"
const metaSeparator = "."
const keySeparator = "."

// MetaDataKey looks like this: fp.topics.0.18
type MetaDataKey string

func MetaDataKeyFromDatabaseVersion(db, version string) MetaDataKey {
return MetaDataKey(strings.Join([]string{fireproofPrefix, db, version}, keySeparator))
}

// Valid returns in the string is valid meta data key
// - must have at least 3 dots (one separates prefix from database, one separates database from version,
// and the version contains one dot itself
func (m MetaDataKey) Valid() bool {
sepCount := strings.Count(string(m), metaSeparator)
sepCount := strings.Count(string(m), keySeparator)
return sepCount >= 3 && strings.HasPrefix(string(m), fireproofPrefix)
}

func (m MetaDataKey) Name() string {
noPrefix := strings.TrimPrefix(string(m), fireproofPrefix+metaSeparator)
lastDot := strings.LastIndex(noPrefix, metaSeparator)
noPrefix := strings.TrimPrefix(string(m), fireproofPrefix+keySeparator)
lastDot := strings.LastIndex(noPrefix, keySeparator)
if lastDot > 0 {
secondLastDot := strings.LastIndex(noPrefix[:lastDot], metaSeparator)
secondLastDot := strings.LastIndex(noPrefix[:lastDot], keySeparator)
if secondLastDot > 0 {
return noPrefix[:secondLastDot]
}
Expand All @@ -29,12 +33,31 @@ func (m MetaDataKey) Name() string {
}

func (m MetaDataKey) Version() string {
lastDot := strings.LastIndex(string(m), metaSeparator)
lastDot := strings.LastIndex(string(m), keySeparator)
if lastDot > 0 {
secondLastDot := strings.LastIndex(string(m), metaSeparator)
secondLastDot := strings.LastIndex(string(m), keySeparator)
if secondLastDot > 0 {
return string(m[secondLastDot-1:])
}
}
return ""
}

// DataKey looks like this: fp.topics
type DataKey string

func DataKeyFromDatabase(db string) MetaDataKey {
return MetaDataKey(strings.Join([]string{fireproofPrefix, db}, keySeparator))
}

// Valid returns in the string is valid meta data key
// - must have at least 1 dot (one separates prefix from database
func (m DataKey) Valid() bool {
sepCount := strings.Count(string(m), keySeparator)
return sepCount >= 1 && strings.HasPrefix(string(m), fireproofPrefix)
}

func (m DataKey) Name() string {
noPrefix := strings.TrimPrefix(string(m), fireproofPrefix+keySeparator)
return noPrefix
}
40 changes: 40 additions & 0 deletions meta_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,43 @@ func TestMetaDataKey(t *testing.T) {
})
}
}

func TestDataKey(t *testing.T) {

tests := []struct {
in DataKey
valid bool
name string
}{
{
in: "fp.topics",
valid: true,
name: "topics",
},
{
in: "fp",
valid: false,
},
{
in: "xp.topics",
valid: false,
},
}

for _, test := range tests {
t.Run(string(test.in), func(t *testing.T) {
valid := test.in.Valid()
if valid != test.valid {
t.Errorf("expected valid %T, got %T", test.valid, valid)
return
}

if valid {
name := test.in.Name()
if name != test.name {
t.Errorf("expected name %s, got %s", test.name, name)
}
}
})
}
}
257 changes: 257 additions & 0 deletions transport_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
package main

import (
"bytes"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/httptest"
"net/url"
"os"
"testing"

"github.com/go-kit/log"
)

func TestDownloadDataHandler(t *testing.T) {
_, svc, _, handler := setupTest(t)

dbName := "db1"
dbKey := DataKeyFromDatabase(dbName)
sum := sha256.Sum256([]byte("fireplace"))
car := fmt.Sprintf("%x.car", sum)
contents := "TOPSECRET"

err := svc.SetData(string(dbKey), car, readCloserString(contents))
if err != nil {
t.Fatalf("error creating test data: %v", err)
}

target := fmt.Sprintf("/api/download/data/%s/%s", dbKey, car)
req := httptest.NewRequest(http.MethodGet, target, nil)
rr := httptest.NewRecorder()
handler.ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Errorf("expected status %d got %d", http.StatusOK, rr.Code)
}

data, err := io.ReadAll(rr.Body)
if err != nil {
t.Fatalf("error reading response body: %v", err)
}
if string(data) != contents {
t.Errorf("expected %q, got %q", contents, string(data))
}
}

func TestUploadDataHandler(t *testing.T) {
_, svc, _, handler := setupTest(t)

dbName := "db1"
dbKey := DataKeyFromDatabase(dbName)
sum := sha256.Sum256([]byte("fireplace"))
car := fmt.Sprintf("%x.car", sum)
contents := "TOPSECRET"

target := fmt.Sprintf("/api/upload/data/%s/%s", dbKey, car)
req := httptest.NewRequest(http.MethodPut, target, readCloserString(contents))
rr := httptest.NewRecorder()
handler.ServeHTTP(rr, req)
if rr.Code != http.StatusCreated {
t.Errorf("expected status %d got %d", http.StatusCreated, rr.Code)
}

actual, err := stringErrReadCloserErr(svc.Data(string(dbKey), car))
if err != nil {
t.Fatalf("error reading test data: %v", err)
}
if actual != contents {
t.Errorf("expected %q, got %q", contents, actual)
}

}

func TestPrepareUploadData(t *testing.T) {

_, _, _, handler := setupTest(t)

dbName := "db1"
dbKey := DataKeyFromDatabase(dbName)
sum := sha256.Sum256([]byte("fireplace"))
carNoExt := fmt.Sprintf("%x", sum)

prepareUploadTarget := dataPrepareUploadRequest(string(dbKey), carNoExt)
req := httptest.NewRequest(http.MethodGet, prepareUploadTarget, nil)
rr := httptest.NewRecorder()
handler.ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Errorf("expected status %d got %d", http.StatusOK, rr.Code)
}

var res UploadRequestResponse
err := json.NewDecoder(rr.Body).Decode(&res)
if err != nil {
t.Fatalf("error decoding json: %v", err)
}
expectURL := fmt.Sprintf("/api/upload/data/%s/%s.car", dbKey, carNoExt)
if res.UploadURL != expectURL {
t.Errorf("expected %q, got %q", expectURL, res.UploadURL)
}
expectKey := fmt.Sprintf("data/%s/%s.car", dbKey, carNoExt)
if res.Key != expectKey {
t.Errorf("expected %q, got %q", expectKey, res.Key)
}
}

func TestDownloadMeta(t *testing.T) {
_, svc, _, handler := setupTest(t)

dbName := "db1"
dbKey := MetaDataKeyFromDatabaseVersion(dbName, "0.18")
sum := sha256.Sum256([]byte("fireplace"))
carNoExt := fmt.Sprintf("%x", sum)
contents := "TOPSECRET"

mr := &MetaRequest{
CID: carNoExt,
Data: base64.StdEncoding.EncodeToString([]byte(contents)),
Parents: nil,
}
mrBytes, err := json.Marshal(mr)
if err != nil {
t.Fatalf("error encoding json: %v", err)
}
err = svc.SetMeta(string(dbKey), "", mr, mrBytes)
if err != nil {
t.Fatalf("error setting meta for test: %v", err)
}

getMetaTarget := metaGetRequest(string(dbKey), "")
req := httptest.NewRequest(http.MethodGet, getMetaTarget, nil)
rr := httptest.NewRecorder()
handler.ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Errorf("expected status %d got %d", http.StatusOK, rr.Code)
}

var res MetaResponse
err = json.NewDecoder(rr.Body).Decode(&res)
if err != nil {
t.Fatalf("error decoding json: %v", err)
}
if res.Status != 200 {
t.Errorf("expected meta response status 200, got %d", res.Status)
}

var inner MetaItems
err = json.Unmarshal([]byte(res.Body), &inner)
if err != nil {
t.Fatalf("error decoding inner json: %v", err)
}

if len(inner.Items) != 1 {
t.Fatalf("expected 1 item, got %d", len(inner.Items))
}

if inner.Items[0].CID != mr.CID {
t.Errorf("expected CID %q got %q", mr.CID, inner.Items[0].CID)
}
if inner.Items[0].Data != mr.Data {
t.Errorf("expeced data %q got %q", mr.Data, inner.Items[0].Data)
}
}

func TestUploadMeta(t *testing.T) {
_, svc, _, handler := setupTest(t)

dbName := "db1"
dbKey := MetaDataKeyFromDatabaseVersion(dbName, "0.18")
sum := sha256.Sum256([]byte("fireplace"))
carNoExt := fmt.Sprintf("%x", sum)
contents := "TOPSECRET"

mr := &MetaRequest{
CID: carNoExt,
Data: base64.StdEncoding.EncodeToString([]byte(contents)),
Parents: nil,
}
mrBytes, err := json.Marshal(mr)
if err != nil {
t.Fatalf("error encoding json: %v", err)
}

getMetaTarget := metaGetRequest(string(dbKey), "")
req := httptest.NewRequest(http.MethodPut, getMetaTarget, bytes.NewBuffer(mrBytes))
rr := httptest.NewRecorder()
handler.ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Errorf("expected status %d got %d", http.StatusOK, rr.Code)
}

metaReqs, err := svc.Meta(string(dbKey), "")
if err != nil {
t.Fatalf("error loading meta data: %v", err)
}
if len(metaReqs) != 1 {
t.Fatalf("expected 1 meta, got %d", len(metaReqs))
}

if metaReqs[0].CID != mr.CID {
t.Errorf("expected CID %q got %q", mr.CID, metaReqs[0].CID)
}
if metaReqs[0].Data != mr.Data {
t.Errorf("expeced data %q got %q", mr.Data, metaReqs[0].Data)
}
}

func metaGetRequest(name, branch string) string {
u := url.URL{
Path: "/api/upload",
}
q := u.Query()
q.Set("type", "meta")
q.Set("name", name)
q.Set("branch", branch)
u.RawQuery = q.Encode()

return u.String()
}

func dataPrepareUploadRequest(name, car string) string {
u := url.URL{
Path: "/api/upload",
}
q := u.Query()
q.Set("type", "data")
q.Set("name", name)
q.Set("car", car)
u.RawQuery = q.Encode()

return u.String()
}

func setupTest(t *testing.T) (log.Logger, *service, *Application, http.Handler) {
var logger log.Logger
{
logger = log.NewLogfmtLogger(os.Stderr)
logger = log.With(logger, "ts", log.DefaultTimestampUTC)
logger = log.With(logger, "caller", log.DefaultCaller)
}

dataStore := NewMemoryDataStore()
metaStore := NewMemoryMetaStore()
svc := newService(logger, dataStore, metaStore, "", "", false)

testApp := &Application{
Name: "testapp",
AutoStart: false,
LocalPath: t.TempDir(),
}

handler := makeHandlerForApplication(testApp, svc, nil, logger)

return logger, svc, testApp, handler
}

0 comments on commit 652d9f9

Please sign in to comment.