impl many integration tests
parent
3b72f05b4e
commit
360a686906
4
main.go
4
main.go
|
|
@ -7,7 +7,9 @@ import (
|
||||||
"log"
|
"log"
|
||||||
)
|
)
|
||||||
|
|
||||||
var GitCommit string
|
var (
|
||||||
|
GitCommit string
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
c := config.New()
|
c := config.New()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -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) {
|
func makeTokenForNamespace(ctx context.Context, g storage.RateLimitedGraph, namespace string) (Token, error) {
|
||||||
token := Token{
|
token := Token{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Token: uuid.New().String(),
|
ID: uuid.New().String(),
|
||||||
}
|
}
|
||||||
obf, err := token.Obfuscate()
|
obf, err := token.Obfuscate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Token{}, err
|
return Token{}, err
|
||||||
}
|
}
|
||||||
one := entity.One{
|
one := entity.One{
|
||||||
ID: token.Token,
|
ID: token.ID,
|
||||||
Title: obf,
|
Title: obf,
|
||||||
}
|
}
|
||||||
return token, g.Insert(ctx, namespace+"."+AuthKey, one)
|
return token, g.Insert(ctx, namespace+"."+AuthKey, one)
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,12 @@ import (
|
||||||
|
|
||||||
type Token struct {
|
type Token struct {
|
||||||
Namespace string
|
Namespace string
|
||||||
Token string
|
ID string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Token) String() string {
|
||||||
|
s, _ := t.Obfuscate()
|
||||||
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t Token) Obfuscate() (string, error) {
|
func (t Token) Obfuscate() (string, error) {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import (
|
||||||
func TestTokenEncDec(t *testing.T) {
|
func TestTokenEncDec(t *testing.T) {
|
||||||
token := Token{
|
token := Token{
|
||||||
Namespace: "username",
|
Namespace: "username",
|
||||||
Token: uuid.New().String(),
|
ID: uuid.New().String(),
|
||||||
}
|
}
|
||||||
key := "a"
|
key := "a"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ func getToken(r *http.Request) (Token, bool) {
|
||||||
if !config.New().Auth {
|
if !config.New().Auth {
|
||||||
namespaces, ok := r.URL.Query()["ns"]
|
namespaces, ok := r.URL.Query()["ns"]
|
||||||
if ok && len(namespaces) > 0 {
|
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)
|
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 {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,12 @@ func TestVerify(t *testing.T) {
|
||||||
fresh := func() (storage.RateLimitedGraph, *httptest.ResponseRecorder, *http.Request, Token, string) {
|
fresh := func() (storage.RateLimitedGraph, *httptest.ResponseRecorder, *http.Request, Token, string) {
|
||||||
g := storage.NewRateLimitedGraph()
|
g := storage.NewRateLimitedGraph()
|
||||||
token := Token{
|
token := Token{
|
||||||
Token: uuid.New().String(),
|
ID: uuid.New().String(),
|
||||||
Namespace: uuid.New().String(),
|
Namespace: uuid.New().String(),
|
||||||
}
|
}
|
||||||
obf, _ := token.Obfuscate()
|
obf, _ := token.Obfuscate()
|
||||||
one := entity.One{
|
one := entity.One{
|
||||||
ID: token.Token,
|
ID: token.ID,
|
||||||
Title: obf,
|
Title: obf,
|
||||||
}
|
}
|
||||||
if err := g.Insert(context.TODO(), token.Namespace+"."+AuthKey, one); err != nil {
|
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) {
|
t.Run("bad auth", func(t *testing.T) {
|
||||||
g, w, r, token, _ := fresh()
|
g, w, r, token, _ := fresh()
|
||||||
token.Token = uuid.New().String()
|
token.ID = uuid.New().String()
|
||||||
obf, err := token.Obfuscate()
|
obf, err := token.Obfuscate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
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 {
|
if err := g.Insert(context.TODO(), token.Namespace, entity.One{ID: UserKey}); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
token.Token = "gibberish-but-public-ns-so-its-ok"
|
token.ID = "gibberish-but-public-ns-so-its-ok"
|
||||||
obf, _ := token.Obfuscate()
|
obf, _ := token.Obfuscate()
|
||||||
r.AddCookie(&http.Cookie{
|
r.AddCookie(&http.Cookie{
|
||||||
Name: AuthKey,
|
Name: AuthKey,
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ func NewREST(g storage.RateLimitedGraph) (*REST, error) {
|
||||||
bar := foo
|
bar := foo
|
||||||
bar = rest.shift(bar)
|
bar = rest.shift(bar)
|
||||||
bar = rest.scoped(bar)
|
bar = rest.scoped(bar)
|
||||||
if !strings.HasPrefix(path, "users/") {
|
if !strings.HasPrefix(path, "users/") && path != "version" {
|
||||||
bar = rest.auth(bar)
|
bar = rest.auth(bar)
|
||||||
}
|
}
|
||||||
bar = rest.defend(bar)
|
bar = rest.defend(bar)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue