dndex/main_test.go

320 lines
7.6 KiB
Go

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
}