Skip to content
Snippets Groups Projects
Commit adfa0ef9 authored by Niamh Nikali's avatar Niamh Nikali
Browse files

Add contact package from client

parent 4a62c355
Branches
Tags
No related merge requests found
package contact
import (
"encoding/json"
"github.com/pkg/errors"
jww "github.com/spf13/jwalterweatherman"
"gitlab.com/xx_network/primitives/id"
"strings"
)
const factDelimiter = ","
const factBreak = ";"
// Contact implements the Contact interface defined in interface/contact.go,
// in go, the structure is meant to be edited directly, the functions are for
// bindings compatibility
type Contact struct {
ID *id.ID
DhPubKey []byte
OwnershipProof []byte
Facts []Fact
}
// GetID returns the user ID for this user.
func (c Contact) GetID() []byte {
return c.ID.Bytes()
}
// GetDHPublicKey returns the public key associated with the Contact.
func (c Contact) GetDHPublicKey() []byte {
return c.DhPubKey
}
// GetDHPublicKey returns hash of a DH proof of key ownership.
func (c Contact) GetOwnershipProof() []byte {
return c.OwnershipProof
}
// Returns a fact list for adding and getting facts to and from the contact
func (c Contact) GetFactList() FactList {
return FactList{source: &c}
}
// json marshals the contact
func (c Contact) Marshal() ([]byte, error) {
return json.Marshal(&c)
}
// converts facts to a delineated string with an ending character for transfer
// over the network
func (c Contact) StringifyFacts() string {
stringList := make([]string, len(c.Facts))
for index, f := range c.Facts {
stringList[index] = f.Stringify()
}
return strings.Join(stringList, factDelimiter) + factBreak
}
func Unmarshal(b []byte) (Contact, error) {
c := Contact{}
err := json.Unmarshal(b, &c)
if err != nil {
return c, err
}
for i, fact := range c.Facts {
if !fact.T.IsValid() {
return Contact{}, errors.Errorf("Fact %v/%v has invalid "+
"type: %s", i, len(c.Facts), fact.T)
}
}
return c, nil
}
// splits the "facts" portion of the payload from the rest and returns them as
// facts
func UnstringifyFacts(s string) ([]Fact, string, error) {
parts := strings.SplitN(s, factBreak, 1)
if len(parts) != 2 {
return nil, "", errors.New("Invalid fact string passed")
}
factStrings := strings.Split(parts[0], factDelimiter)
var factList []Fact
for _, fString := range factStrings {
fact, err := UnstringifyFact(fString)
if err != nil {
jww.WARN.Printf("Fact failed to unstringify, dropped: %s",
err)
} else {
factList = append(factList, fact)
}
}
return factList, parts[1], nil
}
package contact
import (
"encoding/json"
"gitlab.com/xx_network/primitives/id"
"reflect"
"testing"
)
// Test that GetID returns an id.ID in the Contact
func TestContact_GetID(t *testing.T) {
C := Contact{ID: &id.DummyUser}
if !reflect.DeepEqual(C.GetID(), id.DummyUser.Bytes()) {
t.Error("Did not return the correct ID")
}
}
// Test that GetDHPublicKey returns the cyclic int in Contact
func TestContact_GetDHPublicKey(t *testing.T) {
// TODO: Find a test key
C := Contact{DhPubKey: []byte{5}}
dh := C.GetDHPublicKey()
if !reflect.DeepEqual(dh, []byte{5}) {
t.Error("Returned DH key did not match expected DH key")
}
}
// Test that GetOwnershipProof returns the []byte in Contact
func TestContact_GetOwnershipProof(t *testing.T) {
C := Contact{OwnershipProof: []byte{30, 40, 50}}
if !reflect.DeepEqual([]byte{30, 40, 50}, C.GetOwnershipProof()) {
t.Error("Returned proof key did not match expected proof")
}
}
// Test that GetFactList returns a FactList and our Fact is in it
func TestContact_GetFactList(t *testing.T) {
FL := new([]Fact)
C := Contact{Facts: *FL}
C.Facts = append(C.Facts, Fact{
Fact: "testing",
T: Phone,
})
gFL := C.GetFactList()
if !reflect.DeepEqual(C.Facts[0], gFL.Get(0)) {
t.Error("Expected Fact and got Fact did not match")
}
}
// Test that Marshal can complete without error, the output should
// be verified with the test below
func TestContact_Marshal(t *testing.T) {
C := Contact{
ID: &id.DummyUser,
DhPubKey: []byte{5},
OwnershipProof: []byte{30, 40, 50},
Facts: []Fact{{
Fact: "testing",
T: Email,
}},
}
M, err := C.Marshal()
if err != nil {
t.Error(err)
}
t.Log(M)
}
// Test that Unmarshal successfully unmarshals the Contact marshalled
// above
func TestContact_Unmarshal(t *testing.T) {
// Expected contact
E := Contact{
ID: &id.DummyUser,
DhPubKey: []byte{5},
OwnershipProof: []byte{30, 40, 50},
Facts: []Fact{{
Fact: "testing",
T: Email,
}},
}
// Marshalled contact, gotten from above test
M := []byte{123, 34, 73, 68, 34, 58, 91, 49, 48, 48, 44, 49, 49, 55,
44, 49, 48, 57, 44, 49, 48, 57, 44, 49, 50, 49, 44, 48, 44, 48,
44, 48, 44, 48, 44, 48, 44, 48, 44, 48, 44, 48, 44, 48, 44, 48,
44, 48, 44, 48, 44, 48, 44, 48, 44, 48, 44, 48, 44, 48, 44, 48,
44, 48, 44, 48, 44, 48, 44, 48, 44, 48, 44, 48, 44, 48, 44, 48,
44, 48, 44, 51, 93, 44, 34, 68, 104, 80, 117, 98, 75, 101, 121,
34, 58, 123, 125, 44, 34, 79, 119, 110, 101, 114, 115, 104, 105,
112, 80, 114, 111, 111, 102, 34, 58, 34, 72, 105, 103, 121, 34,
44, 34, 70, 97, 99, 116, 115, 34, 58, 91, 123, 34, 70, 97, 99,
116, 34, 58, 34, 116, 101, 115, 116, 105, 110, 103, 34, 44, 34,
84, 34, 58, 49, 125, 93, 125}
C, err := Unmarshal(M)
if err != nil {
t.Error(err)
}
if !reflect.DeepEqual(C, E) {
t.Error("Expected and Unmarshaled contact are not equal")
}
}
// Test that Unmarshalling a contact with a bad FactType Fact fails
func TestContact_UnmarshalBadFact(t *testing.T) {
C := Contact{Facts: []Fact{{
Fact: "testing",
T: 200,
}}}
M, err := json.Marshal(&C)
if err != nil {
t.Error(err)
}
_, err = Unmarshal(M)
if err == nil {
t.Error("Unmarshalling Contact containing Fact with an invalid Fact type should've errored")
}
}
// Test that StringifyFacts can complete without error,
// the output should be verified with the test below
func TestContact_StringifyFacts(t *testing.T) {
C := Contact{Facts: []Fact{
{
Fact: "testing",
T: Phone,
},
{
Fact: "othertest",
T: Email,
},
}}
S := C.StringifyFacts()
t.Log(S)
}
// Test that UnstringifyFacts successfully unstingify-ies
// the FactList stringified above
// NOTE: This test does not pass, for reason "Invalid fact string passed"
func TestUnstringifyFacts(t *testing.T) {
E := Contact{Facts: []Fact{
{
Fact: "testing",
T: Phone,
},
{
Fact: "othertest",
T: Email,
},
}}
FL, S, err := UnstringifyFacts("Ptesting,Eothertest;")
if err != nil {
t.Error(err)
}
t.Log(S)
if !reflect.DeepEqual(E.Facts, FL) {
t.Error("Expected FactList and got FactList do not match")
}
}
package contact
import "errors"
type Fact struct {
Fact string
T FactType
}
func NewFact(ft FactType, fact string) (Fact, error) {
//todo: filter the fact string
return Fact{
Fact: fact,
T: ft,
}, nil
}
func (f Fact) Get() string {
return f.Fact
}
func (f Fact) Type() int {
return int(f.T)
}
// marshal is for transmission for UDB, not a part of the fact interface
func (f Fact) Stringify() string {
return f.T.Stringify() + f.Fact
}
func UnstringifyFact(s string) (Fact, error) {
ft, err := UnstringifyFactType(s)
if err != nil {
return Fact{}, err
}
if len(s) < 1 {
return Fact{}, errors.New("cannot unstringify a fact that's just its type")
}
return NewFact(ft, s[1:])
}
package contact
import (
"github.com/pkg/errors"
)
type FactList struct {
source *Contact
}
func (fl FactList) Num() int {
return len(fl.source.Facts)
}
func (fl FactList) Get(i int) Fact {
return fl.source.Facts[i]
}
func (fl FactList) Add(fact string, factType int) error {
ft := FactType(factType)
if !ft.IsValid() {
return errors.New("Invalid fact type")
}
f, err := NewFact(ft, fact)
if err != nil {
return err
}
fl.source.Facts = append(fl.source.Facts, f)
return nil
}
package contact
import (
"reflect"
"testing"
)
// Test that the num function returns the correct number of Facts
func TestFactList_Num(t *testing.T) {
e1 := Fact{
Fact: "testing",
T: Phone,
}
e2 := Fact{
Fact: "othertest",
T: Email,
}
Fs := new([]Fact)
C := Contact{Facts: *Fs}
FL := FactList{source: &C}
FL.source.Facts = append(FL.source.Facts, e1)
FL.source.Facts = append(FL.source.Facts, e2)
if FL.Num() != 2 {
t.Error("Num returned incorrect number of Facts in FactList")
}
}
// Test the get function gets the right Fact at the index
func TestFactList_Get(t *testing.T) {
e := Fact{
Fact: "testing",
T: Phone,
}
Fs := new([]Fact)
C := Contact{Facts: *Fs}
FL := FactList{source: &C}
FL.source.Facts = append(FL.source.Facts, e)
if !reflect.DeepEqual(e, FL.Get(0)) {
t.Error("Expected fact does not match got fact")
}
}
// Test the add function adds a Fact to the source correctly
func TestFactList_Add(t *testing.T) {
// Expected fact
e := Fact{
Fact: "testing",
T: Phone,
}
Fs := new([]Fact)
C := Contact{Facts: *Fs}
FL := FactList{source: &C}
err := FL.Add("testing", int(Phone))
if err != nil {
t.Error(err)
}
if !reflect.DeepEqual(e, FL.source.Facts[0]) {
t.Error("Expected fact does not match added fact")
}
}
func TestFactList_AddInvalidType(t *testing.T) {
Fs := new([]Fact)
C := Contact{Facts: *Fs}
FL := FactList{source: &C}
err := FL.Add("testing", 200)
if err == nil {
t.Error("Adding a Fact to FactList with type 200 did not return an error!")
}
}
package contact
import (
"reflect"
"testing"
)
// Test NewFact() function returns a correctly formatted Fact
func TestNewFact(t *testing.T) {
// Expected result
e := Fact{
Fact: "testing",
T: 1,
}
g, err := NewFact(Email, "testing")
if err != nil {
t.Error(err)
}
if !reflect.DeepEqual(e, g) {
t.Errorf("The returned Fact did not match the expected Fact")
}
}
// Test Get() function correctly gets the fact string
func TestFact_Get(t *testing.T) {
f := Fact{
Fact: "testing",
T: 1,
}
if f.Get() != f.Fact {
t.Errorf("f.Get() did not return the same value as f.Fact")
}
}
// Test Type() function correctly gets the type number
func TestFact_Type(t *testing.T) {
f := Fact{
Fact: "testing",
T: 1,
}
if f.Type() != int(f.T) {
t.Errorf("f.Type() did not return the same value as int(f.T)")
}
}
// Test Stringify() creates a string of the Fact
// The output is verified to work in the test below
func TestFact_Stringify(t *testing.T) {
f := Fact{
Fact: "testing",
T: 1,
}
expected := "Etesting"
got := f.Stringify()
t.Log(got)
if got != expected {
t.Errorf("Marshalled object from Got did not match Expected.\n\tGot: %v\n\tExpected: %v", got, expected)
}
}
// Test the UnstringifyFact function creates a Fact from a string
// NOTE: this test does not pass, with error "Unknown Fact FactType: Etesting"
func TestFact_UnstringifyFact(t *testing.T) {
// Expected fact from above test
e := Fact{
Fact: "testing",
T: Email,
}
// Stringify-ed Fact from above test
m := "Etesting"
f, err := UnstringifyFact(m)
if err != nil {
t.Error(err)
}
t.Log(f.Fact)
t.Log(f.T)
if !reflect.DeepEqual(e, f) {
t.Errorf("The returned Fact did not match the expected Fact")
}
}
package contact
import (
"fmt"
"github.com/pkg/errors"
jww "github.com/spf13/jwalterweatherman"
)
type FactType uint8
const (
Username FactType = 0
Email FactType = 1
Phone FactType = 2
)
func (t FactType) String() string {
switch t {
case Username:
return "Username"
case Email:
return "Email"
case Phone:
return "Phone"
default:
return fmt.Sprintf("Unknown Fact FactType: %d", t)
}
}
func (t FactType) Stringify() string {
switch t {
case Username:
return "U"
case Email:
return "E"
case Phone:
return "P"
}
jww.FATAL.Panicf("Unknown Fact FactType: %d", t)
return "error"
}
func UnstringifyFactType(s string) (FactType, error) {
switch s[0] {
case 'U':
return Username, nil
case 'E':
return Email, nil
case 'P':
return Phone, nil
}
return 3, errors.Errorf("Unknown Fact FactType: %s", s)
}
func (t FactType) IsValid() bool {
return t == Username || t == Email || t == Phone
}
package contact
import (
"testing"
)
func TestFactType_String(t *testing.T) {
// FactTypes and expected strings for them
FTs := []FactType{Username, Email, Phone, FactType(200)}
Strs := []string{"Username", "Email", "Phone", "Unknown Fact FactType: 200"}
for i, ft := range FTs {
if FactType.String(ft) != Strs[i] {
t.Errorf("Got unexpected string for FactType.\n\tGot: %s\n\tExpected: %s", FactType.String(ft), Strs[i])
}
}
}
func TestFactType_Stringify(t *testing.T) {
// FactTypes and expected strings for them
FTs := []FactType{Username, Email, Phone}
Strs := []string{"U", "E", "P"}
for i, ft := range FTs {
if FactType.Stringify(ft) != Strs[i] {
t.Errorf("Got unexpected string for FactType.\n\tGot: %s\n\tExpected: %s", FactType.Stringify(ft), Strs[i])
}
}
}
func TestFactType_Unstringify(t *testing.T) {
// FactTypes and expected strings for them
FTs := []FactType{Username, Email, Phone}
Strs := []string{"U", "E", "P"}
for i, ft := range FTs {
gotft, err := UnstringifyFactType(Strs[i])
if err != nil {
t.Error(err)
}
if gotft != ft {
t.Errorf("Got unexpected string for FactType.\n\tGot: %s\n\tExpected: %s", FactType.Stringify(ft), Strs[i])
}
}
_, err := UnstringifyFactType("x")
if err == nil {
t.Errorf("UnstringifyFactType did not return an error on an invalid type")
}
}
func TestFactType_IsValid(t *testing.T) {
if !FactType.IsValid(Username) ||
!FactType.IsValid(Email) ||
!FactType.IsValid(Phone) {
t.Errorf("FactType.IsValid did not report a FactType as valid")
}
if FactType.IsValid(FactType(200)) {
t.Errorf("FactType.IsValid reported a non-valid FactType value as valid")
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment