diff --git a/fact/fact.go b/fact/fact.go new file mode 100644 index 0000000000000000000000000000000000000000..5771376cc168b14d9888d79f4b6eda36e907a89e --- /dev/null +++ b/fact/fact.go @@ -0,0 +1,35 @@ +package fact + +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 +} + +// 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) { + if len(s) < 1 { + return Fact{}, errors.New("stringified facts must at least have a type at the start") + } + T := s[:1] + fact := s[1:] + ft, err := UnstringifyFactType(T) + if err != nil { + return Fact{}, err + } + + return NewFact(ft, fact) +} diff --git a/fact/factList.go b/fact/factList.go new file mode 100644 index 0000000000000000000000000000000000000000..21d0edbdde09a3d1cba1876ceac3a1ba2252b9bd --- /dev/null +++ b/fact/factList.go @@ -0,0 +1,44 @@ +package fact + +import ( + "github.com/pkg/errors" + jww "github.com/spf13/jwalterweatherman" + "strings" +) + +type FactList []Fact + +const factDelimiter = "," +const factBreak = ";" + +func (fl FactList) Stringify() string { + stringList := make([]string, len(fl)) + for index, f := range fl { + stringList[index] = f.Stringify() + } + + return strings.Join(stringList, factDelimiter) + factBreak +} + +// unstrignifys facts followed by a facts break and with arbatrary data +// atttached at the end +func UnstringifyFactList(s string) (FactList, string, error) { + parts := strings.SplitN(s, factBreak, 2) + 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 +} diff --git a/fact/factList_test.go b/fact/factList_test.go new file mode 100644 index 0000000000000000000000000000000000000000..c9ca485aee26cba37bae3614bd6a16aa3c44654a --- /dev/null +++ b/fact/factList_test.go @@ -0,0 +1,30 @@ +package fact + +import ( + "reflect" + "testing" +) + +func TestFactList_StringifyUnstringify(t *testing.T) { + expected := FactList{} + expected = append(expected, Fact{ + Fact: "vivian@elixxir.io", + T: Email, + }) + expected = append(expected, Fact{ + Fact: "(270) 301-5797", + T: Phone, + }) + + FlString := expected.Stringify() + // Manually check and verify that the string version is as expected + t.Log(FlString) + + actual, _, err := UnstringifyFactList(FlString) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(actual, expected) { + t.Error("fact lists weren't equal") + } +} diff --git a/fact/fact_test.go b/fact/fact_test.go new file mode 100644 index 0000000000000000000000000000000000000000..b89e12c042abf1bbff7a9b2272a0485f7b4ba313 --- /dev/null +++ b/fact/fact_test.go @@ -0,0 +1,65 @@ +package fact + +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 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") + } +} diff --git a/fact/type.go b/fact/type.go new file mode 100644 index 0000000000000000000000000000000000000000..32a48e3614da00cb720a62fdc2e30cb5da5976cd --- /dev/null +++ b/fact/type.go @@ -0,0 +1,57 @@ +package fact + +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 { + 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 +} diff --git a/fact/type_test.go b/fact/type_test.go new file mode 100644 index 0000000000000000000000000000000000000000..3a1e7d136a8735d9fdeaa5dc088a695f786bf080 --- /dev/null +++ b/fact/type_test.go @@ -0,0 +1,60 @@ +package fact + +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") + } +}