impl many integration tests

master
breel 2020-08-09 13:16:28 -06:00
parent 3b72f05b4e
commit 360a686906
8 changed files with 338 additions and 12 deletions

View File

@ -7,7 +7,9 @@ import (
"log"
)
var GitCommit string
var (
GitCommit string
)
func main() {
c := config.New()

319
main_test.go Normal file
View File

@ -0,0 +1,319 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"local/dndex/server/auth"
"local/dndex/storage/entity"
"net/http"
"net/http/httptest"
"os"
"strings"
"testing"
"time"
"github.com/google/uuid"
)
func Test(t *testing.T) {
GitCommit = uuid.New().String()
d, err := ioutil.TempDir(os.TempDir(), "test.dndex")
if err != nil {
t.Fatal(err)
}
s := httptest.NewServer(http.HandlerFunc(nil))
s.Close()
p := strings.Split(s.URL, ":")[2]
os.Args = strings.Split(fmt.Sprintf(`dndex -auth=true -database db -delay 5ms -driver-type map -fileprefix /files -fileroot %s -p %v -rps 50 -sys-rps 40`, d, p), " ")
go main()
for {
resp, err := http.Get(s.URL)
if err == nil {
resp.Body.Close()
break
}
time.Sleep(time.Millisecond * 100)
}
token := register(t, s.URL)
t.Logf("token: %q", token)
createDelete(t, s.URL, token)
createUpdate(t, s.URL, token)
callVersion(t, s.URL)
muckedToken(t, s.URL, token)
createDeleteSub(t, s.URL, token)
createUpdateSub(t, s.URL, token)
filesCRUD(t, s.URL, token)
}
func createDeleteSub(t *testing.T, uri, token string) {
t.Error("not impl")
}
func createUpdateSub(t *testing.T, uri, token string) {
t.Error("not impl")
}
func filesCRUD(t *testing.T, uri, token string) {
t.Error("not impl")
}
func register(t *testing.T, uri string) string {
callList(t, uri, "", http.StatusUnauthorized)
ns, pwd := callRegister(t, uri)
token := callLogin(t, uri, ns, pwd)
callList(t, uri, token, http.StatusOK)
return token
}
func muckedToken(t *testing.T, uri, obf string) {
var token auth.Token
if err := token.Deobfuscate(obf); err != nil {
t.Fatal(err)
}
if token.Namespace == "" || token.ID == "" {
t.Fatalf("ns: %q, id: %q", token.Namespace, token.ID)
}
namespace := token.Namespace
id := token.ID
for _, token := range []auth.Token{
auth.Token{
Namespace: namespace,
ID: uuid.New().String(),
},
auth.Token{
Namespace: uuid.New().String(),
ID: id,
},
} {
callList(t, uri, token.String(), http.StatusUnauthorized)
}
}
func createUpdate(t *testing.T, uri, token string) {
id := callCreate(t, uri, token)
callUpdate(t, uri, token, id)
}
func createDelete(t *testing.T, uri, token string) {
id := callCreate(t, uri, token)
callDelete(t, uri, token, id)
one := callGet(t, uri, token, id, http.StatusNotFound)
if one.ID == id {
t.Fatal(one)
}
if fmt.Sprint(one) != fmt.Sprint(entity.One{}) {
t.Fatal(one)
}
}
func callVersion(t *testing.T, uri string) {
resp := call(t, "", http.MethodGet, uri+"/version", "")
if resp.StatusCode != http.StatusOK {
t.Fatal(resp.StatusCode)
}
var response struct {
Version string `json:"version"`
}
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatal(err)
}
if err := json.Unmarshal(b, &response); err != nil {
t.Fatal(err)
}
if response.Version != GitCommit {
t.Fatalf("%s: %v", b, response)
}
}
func callGet(t *testing.T, uri, token, id string, status int) entity.One {
resp := call(t, token, http.MethodGet, uri+"/entities/"+id, "")
if resp.StatusCode != status {
t.Fatal(resp.StatusCode)
}
var one entity.One
if err := json.NewDecoder(resp.Body).Decode(&one); err != nil {
t.Fatal(err)
}
return one
}
func callDelete(t *testing.T, uri, token, id string) {
resp := call(t, token, http.MethodDelete, uri+"/entities/"+id, "")
if resp.StatusCode != http.StatusOK {
t.Fatal(resp.StatusCode)
}
}
func callUpdate(t *testing.T, uri, token, id string) {
for _, method := range []string{http.MethodPut, http.MethodPatch} {
m := map[string]interface{}{
"name": "name-" + uuid.New().String()[:5],
"type": "type-" + uuid.New().String()[:5],
"title": "titl-" + uuid.New().String()[:5],
"text": "text-" + uuid.New().String()[:5],
"connections": map[string]map[string]string{
"???": map[string]string{"relationship": ":?"},
},
"attachments": map[string]map[string]string{
"myfile": map[string]string{"location": "/files/my_file_location.txt"},
},
}
b, err := json.Marshal(m)
if err != nil {
t.Fatal(err)
}
resp := call(t, token, method, uri+"/entities/"+id, string(b))
if resp.StatusCode != http.StatusOK {
t.Fatal(resp.StatusCode)
}
var response map[string]map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
t.Fatal(err)
}
found := false
for k := range response {
for k2 := range m {
if fmt.Sprint(m[k2]) != fmt.Sprint(response[k][k2]) {
t.Fatal(k2, response[k][k2])
}
}
found = true
}
if !found {
t.Fatal(found)
}
}
}
func callCreate(t *testing.T, uri, token string) string {
m := map[string]interface{}{
"name": "name-" + uuid.New().String()[:5],
"type": "type-" + uuid.New().String()[:5],
"title": "titl-" + uuid.New().String()[:5],
"text": "text-" + uuid.New().String()[:5],
"connections": map[string]map[string]string{
"???": map[string]string{"relationship": ":?"},
},
"attachments": map[string]map[string]string{
"myfile": map[string]string{"location": "/files/my_file_location.txt"},
},
}
b, err := json.Marshal(m)
if err != nil {
t.Fatal(err)
}
resp := call(t, token, http.MethodPost, uri+"/entities", string(b))
if resp.StatusCode != http.StatusOK {
t.Fatal(resp.StatusCode)
}
var response map[string]map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
t.Fatal(err)
}
for k := range response {
for k2 := range m {
if fmt.Sprint(m[k2]) != fmt.Sprint(response[k][k2]) {
t.Fatal(k2, response[k][k2])
}
}
return k
}
t.Fatal(response)
panic("how?")
}
func callList(t *testing.T, uri, token string, status int) {
resp := call(t, token, http.MethodGet, uri+"/entities", "")
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatal(err)
}
if resp.StatusCode != status {
t.Fatalf("%v: %s", resp.StatusCode, b)
}
}
func callRegister(t *testing.T, uri string) (string, string) {
ns := uuid.New().String()
pwd := uuid.New().String()
resp, err := http.Post(uri+"/users/register", "application/x-www-form-urlencoded", strings.NewReader(fmt.Sprintf("%s=%s&%s=%s", auth.UserKey, ns, auth.AuthKey, pwd)))
if err != nil {
t.Fatal(err)
}
resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Fatal(resp.StatusCode)
}
return ns, pwd
}
func callLogin(t *testing.T, uri, ns, pwd string) string {
resp, err := http.Post(uri+"/users/login", "application/x-www-form-urlencoded", strings.NewReader(fmt.Sprintf("%s=%s", auth.UserKey, ns)))
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Fatal(resp.StatusCode)
}
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatal(err)
}
var response struct {
OK struct {
EncodedToken string `json:"token"`
Salt string `json:"salt"`
} `json:"ok"`
}
if err := json.Unmarshal(b, &response); err != nil {
t.Fatalf("%v: %s", err, b)
}
salt := response.OK.Salt
encodedToken := response.OK.EncodedToken
if len(salt) == 0 {
t.Fatal("salt empty")
}
if len(encodedToken) == 0 {
t.Fatal("token empty")
}
token := auth.Token{}
if err := token.Decode(salt+pwd, encodedToken); err != nil {
t.Fatal(err)
}
return token.String()
}
func call(t *testing.T, token, method, uri, body string) *http.Response {
r, err := http.NewRequest(method, uri, strings.NewReader(body))
if err != nil {
t.Fatal(err)
}
r.AddCookie(&http.Cookie{Name: auth.AuthKey, Value: token})
resp, err := http.DefaultClient.Do(r)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatal(err)
}
resp.Body = struct {
io.Reader
io.Closer
}{
Closer: resp.Body,
Reader: bytes.NewReader(b),
}
return resp
}

View File

@ -61,14 +61,14 @@ func getKeyForNamespace(ctx context.Context, g storage.RateLimitedGraph, namespa
func makeTokenForNamespace(ctx context.Context, g storage.RateLimitedGraph, namespace string) (Token, error) {
token := Token{
Namespace: namespace,
Token: uuid.New().String(),
ID: uuid.New().String(),
}
obf, err := token.Obfuscate()
if err != nil {
return Token{}, err
}
one := entity.One{
ID: token.Token,
ID: token.ID,
Title: obf,
}
return token, g.Insert(ctx, namespace+"."+AuthKey, one)

View File

@ -7,7 +7,12 @@ import (
type Token struct {
Namespace string
Token string
ID string
}
func (t Token) String() string {
s, _ := t.Obfuscate()
return s
}
func (t Token) Obfuscate() (string, error) {

View File

@ -10,7 +10,7 @@ import (
func TestTokenEncDec(t *testing.T) {
token := Token{
Namespace: "username",
Token: uuid.New().String(),
ID: uuid.New().String(),
}
key := "a"

View File

@ -29,7 +29,7 @@ func getToken(r *http.Request) (Token, bool) {
if !config.New().Auth {
namespaces, ok := r.URL.Query()["ns"]
if ok && len(namespaces) > 0 {
return Token{Namespace: namespaces[0], Token: uuid.New().String()}, true
return Token{Namespace: namespaces[0], ID: uuid.New().String()}, true
}
}
cookie, err := r.Cookie(AuthKey)
@ -55,7 +55,7 @@ func isPublicNamespace(ctx context.Context, g storage.RateLimitedGraph, namespac
}
func verifyToken(token Token, g storage.RateLimitedGraph, r *http.Request) error {
serverTokenContainer, err := g.Get(r.Context(), token.Namespace+"."+AuthKey, token.Token)
serverTokenContainer, err := g.Get(r.Context(), token.Namespace+"."+AuthKey, token.ID)
if err != nil {
return err
}

View File

@ -21,12 +21,12 @@ func TestVerify(t *testing.T) {
fresh := func() (storage.RateLimitedGraph, *httptest.ResponseRecorder, *http.Request, Token, string) {
g := storage.NewRateLimitedGraph()
token := Token{
Token: uuid.New().String(),
ID: uuid.New().String(),
Namespace: uuid.New().String(),
}
obf, _ := token.Obfuscate()
one := entity.One{
ID: token.Token,
ID: token.ID,
Title: obf,
}
if err := g.Insert(context.TODO(), token.Namespace+"."+AuthKey, one); err != nil {
@ -102,7 +102,7 @@ func TestVerify(t *testing.T) {
t.Run("bad auth", func(t *testing.T) {
g, w, r, token, _ := fresh()
token.Token = uuid.New().String()
token.ID = uuid.New().String()
obf, err := token.Obfuscate()
if err != nil {
t.Fatal(err)
@ -133,7 +133,7 @@ func TestVerify(t *testing.T) {
if err := g.Insert(context.TODO(), token.Namespace, entity.One{ID: UserKey}); err != nil {
t.Fatal(err)
}
token.Token = "gibberish-but-public-ns-so-its-ok"
token.ID = "gibberish-but-public-ns-so-its-ok"
obf, _ := token.Obfuscate()
r.AddCookie(&http.Cookie{
Name: AuthKey,

View File

@ -45,7 +45,7 @@ func NewREST(g storage.RateLimitedGraph) (*REST, error) {
bar := foo
bar = rest.shift(bar)
bar = rest.scoped(bar)
if !strings.HasPrefix(path, "users/") {
if !strings.HasPrefix(path, "users/") && path != "version" {
bar = rest.auth(bar)
}
bar = rest.defend(bar)