Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test suite generation #1

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion cmd/main.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,40 @@
package main

import (
"encoding/json"
"flag"
"fmt"
"log/slog"
"os"
"reflect"

"github.com/go-json-experiment/json"
"github.com/go-json-experiment/json/jsontext"
tdw "github.com/nuts-foundation/trustdidweb-go"
)

func main() {
genSet := flag.NewFlagSet("generate", flag.ExitOnError)
genTests := genSet.Bool("tests", false, "generate tests")

if len(os.Args) < 2 {
fmt.Println("provide one of following subcommands: generate or example")
os.Exit(1)
}
switch os.Args[1] {
case genSet.Name():
genSet.Parse(os.Args[2:])
if *genTests {
GenerateTests()
}
case "example":
example()
default:
fmt.Println("provide a subcommand: generate or example")
os.Exit(1)
}
}

func example() {
// Set the log level to debug and writer to stdout
logHandler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug})
slog.SetDefault(slog.New(logHandler))
Expand Down
194 changes: 194 additions & 0 deletions cmd/testsuite.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
package main

import (
"crypto/ed25519"
"fmt"
"log"
"os"
"time"

"github.com/go-json-experiment/json"
"github.com/go-json-experiment/json/jsontext"
"github.com/lestrrat-go/jwx/jwk"
tdw "github.com/nuts-foundation/trustdidweb-go"
)

type TestEntry struct {
Id string `json:"id"`
Type []string `json:"type"`
Purpose string `json:"purpose"`
Input string `json:"input,omitempty"`
Expect string `json:"expect,omitempty"`
SigningKey jwk.Key `json:"signingKey,omitempty"`
Params tdw.LogParams `json:"params,omitempty"`
DIDDocument tdw.DIDDocument `json:"didDocument,omitempty"`
Options TestEntryOptions `json:"options,omitempty"`
}

type TestEntryOptions struct {
SigningTime time.Time `json:"signingTime,format:RFC3339"`
}

const CreationTest = "CreationTest"
const UpdateTest = "UpdateTest"
const VerificationTest = "VerificationTest"
const PositiveEvaluationTest = "PositiveEvaluationTest"
const NegativeEvaluationTest = "NegativeEvaluationTest"

type genEntryFunc func() (entry TestEntry, input tdw.DIDLog, expect tdw.DIDLog, err error)

func GenerateTests() {
entries := []TestEntry{}

for _, gen := range []genEntryFunc{genTC001, genTU001, genTV001, genTV002} {
entry, didLogInput, didLogExpect, err := gen()
if err != nil {
log.Fatal(err)
}

if didLogExpect != nil {
// base the signing time on the actual time of the last entry
entry.Options.SigningTime = didLogExpect[len(didLogExpect)-1].VersionTime
}

if didLogInput != nil {
inputRaw, err := didLogInput.MarshalText()
if err != nil {
log.Fatal(err)
}

if err := os.WriteFile(entry.Input, inputRaw, 0644); err != nil {
log.Fatal(err)
}
}
if didLogExpect != nil {
expectedRaw, err := didLogExpect.MarshalText()
if err != nil {
log.Fatal(err)
}

if err := os.WriteFile(entry.Expect, expectedRaw, 0644); err != nil {
log.Fatal(err)
}
}

entries = append(entries, entry)
}

entriesJson, err := json.Marshal(entries, jsontext.WithIndent(" "))
if err != nil {
log.Fatal(err)
}

if err := os.WriteFile("testdata/manifest.json", entriesJson, 0644); err != nil {
log.Fatal(err)
}
}

func genTC001() (entry TestEntry, input tdw.DIDLog, expect tdw.DIDLog, err error) {

// Create a new document
entry = TestEntry{
Id: "tc001",
Type: []string{CreationTest, PositiveEvaluationTest},
Purpose: "Create a new log",
Expect: "testdata/tc001-expect.json",
Options: TestEntryOptions{},
}

// Create a new signer
signer, err := tdw.NewSigner(tdw.CRYPTO_SUITE_EDDSA_JCS_2022)
if err != nil {
log.Fatal(err)
}
signingKey, err := jwk.New(*signer.(*ed25519.PrivateKey))
if err != nil {
log.Fatal(err)
}
entry.SigningKey = signingKey

doc, err := tdw.NewMinimalDIDDocument("did:tdw:{SCID}:example.com")
if err != nil {
log.Fatal(err)
}
entry.DIDDocument = doc

expect, err = tdw.Create(doc, signer)
if err != nil {
log.Fatal(err)
}

return
}

func genTU001() (entry TestEntry, input tdw.DIDLog, expect tdw.DIDLog, err error) {

firstEntry, _, input, err := genTC001()
if err != nil {
log.Fatal(err)
}

// Create a new document
entry = TestEntry{
Id: "tu001",
Type: []string{UpdateTest, PositiveEvaluationTest},
Purpose: "Update a log with a service",
Input: "testdata/tu001-input.json",
Expect: "testdata/tu001-expect.json",
Options: TestEntryOptions{},
}
jwkKey := firstEntry.SigningKey
entry.SigningKey = jwkKey

signingKey := ed25519.PrivateKey{}
if err := jwkKey.Raw(&signingKey); err != nil {
log.Fatal(err)
}

doc, err := input.Document()
if err != nil {
log.Fatal(err)
}

doc["service"] = []map[string]interface{}{{
"id": fmt.Sprintf("did:tdw:%s:example.com#service-1", input[0].Params.Scid),
"type": "ExampleService",
"serviceEndpoint": "https://example.com/service/1",
}}

entry.DIDDocument = doc

expect, err = input.Update(tdw.LogParams{}, doc, signingKey)
if err != nil {
log.Fatal(err)
}

return
}

func genTV001() (entry TestEntry, input tdw.DIDLog, expect tdw.DIDLog, err error) {
entry = TestEntry{
Id: "tv001",
Type: []string{VerificationTest, PositiveEvaluationTest},
Purpose: "Verify a log",
Input: "testdata/tc001-expect.json",
}
return
}

func genTV002() (entry TestEntry, input tdw.DIDLog, expect tdw.DIDLog, err error) {
_, _, input, err = genTC001()
if err != nil {
log.Fatal(err)
}

input[0].Proof[0].ProofValue = "invalid"

entry = TestEntry{
Id: "tv002",
Type: []string{VerificationTest, NegativeEvaluationTest},
Purpose: "Verify a log with an invalid signature",
Input: "testdata/tv002-input.json",
}
return
}
4 changes: 2 additions & 2 deletions logentry.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (

type LogEntry struct {
VersionId versionId `json:"versionId"`
VersionTime time.Time `json:"versionTime"`
VersionTime time.Time `json:"versionTime,format:RFC3339"`
Params LogParams `json:"params"`
DocState docState `json:"docState,omitempty"`
Proof []Proof `json:"proof,omitempty"`
Expand Down Expand Up @@ -82,7 +82,7 @@ func (l *LogEntry) UnmarshalJSONL(b []byte) error {

// MarshalJSONL returns the JSON-line representation of the log entry
func (l LogEntry) MarshalJSONL() ([]byte, error) {
line := []interface{}{l.VersionId, l.VersionTime, l.Params, l.DocState}
line := []interface{}{l.VersionId, l.VersionTime.Format(time.RFC3339), l.Params, l.DocState}

if len(l.Proof) > 0 {
line = append(line, l.Proof)
Expand Down
1 change: 0 additions & 1 deletion logparams.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,5 +313,4 @@ func (p LogParams) Apply(newParams LogParams) (LogParams, error) {
return LogParams{}, fmt.Errorf("invalid log parameters: %w", err)
}
return res, nil

}
82 changes: 82 additions & 0 deletions testdata/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
[
{
"id": "tc001",
"type": [
"CreationTest",
"PositiveEvaluationTest"
],
"purpose": "Create a new log",
"expect": "testdata/tc001-expect.json",
"signingKey": {
"crv": "Ed25519",
"d": "J3l2VTBrN9GckzX8wt0p_FjJ8TDFdKWWNFM-MuHZWVE",
"kty": "OKP",
"x": "Bm-ZiwCZgDgv8K7xf3eGR77Z3vqItWhd0Np5PftRYvM"
},
"didDocument": {
"@context": [
"https://www.w3.org/ns/did/v1"
],
"id": "did:tdw:{SCID}:example.com"
},
"options": {
"signingTime": "2024-09-06T15:53:15+02:00"
}
},
{
"id": "tu001",
"type": [
"UpdateTest",
"PositiveEvaluationTest"
],
"purpose": "Update a log with a service",
"input": "testdata/tu001-input.json",
"expect": "testdata/tu001-expect.json",
"signingKey": {
"crv": "Ed25519",
"d": "m4EusSPab7ZcfYbZcQbeoLzvZhS4tBp1sCz08qW7I_E",
"kty": "OKP",
"x": "ZtnR3mfJ6_xNrYpwgOX-vfJ_hWIAQvnV1AqDhKJnP24"
},
"didDocument": {
"@context": [
"https://www.w3.org/ns/did/v1"
],
"id": "did:tdw:QmdhbxVLQbjdb2UatyP19k6YsYLmUcgXhoZY2741vodGdG:example.com",
"service": [
{
"id": "did:tdw:QmdhbxVLQbjdb2UatyP19k6YsYLmUcgXhoZY2741vodGdG:example.com#service-1",
"type": "ExampleService",
"serviceEndpoint": "https://example.com/service/1"
}
]
},
"options": {
"signingTime": "2024-09-06T15:53:15+02:00"
}
},
{
"id": "tv001",
"type": [
"VerificationTest",
"PositiveEvaluationTest"
],
"purpose": "Verify a log",
"input": "testdata/tc001-expect.json",
"options": {
"signingTime": "0001-01-01T00:00:00Z"
}
},
{
"id": "tv002",
"type": [
"VerificationTest",
"NegativeEvaluationTest"
],
"purpose": "Verify a log with an invalid signature",
"input": "testdata/tv002-input.json",
"options": {
"signingTime": "0001-01-01T00:00:00Z"
}
}
]
1 change: 1 addition & 0 deletions testdata/tc001-expect.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["1-Qmbm62bMYQLwi3HZ1xe1pjq9fFPEC3jFVxYUwnrnyzqFZ6","2024-09-06T15:53:15+02:00",{"method":"did:tdw:0.3","scid":"QmNtqEgkGSXKZ38ofL6tJafNur83qKc7bHTU8weHjM9Mpn","updateKeys":["z6MketPC32eF8aZtP5WQcQAexaubxtYoBWshNX2TYrc7yfbg"]},{"value":{"@context":["https://www.w3.org/ns/did/v1"],"id":"did:tdw:QmNtqEgkGSXKZ38ofL6tJafNur83qKc7bHTU8weHjM9Mpn:example.com"}},[{"challenge":"1-Qmbm62bMYQLwi3HZ1xe1pjq9fFPEC3jFVxYUwnrnyzqFZ6","created":"2024-09-06T15:53:15+02:00","cryptosuite":"eddsa-jcs-2022","proofPurpose":"authentication","proofValue":"z5e5L8UZnS88qNfwP9AuBRZjr1idxfWkQKgZvgqAXMorjgJmCS1ZnHbbcVZ4FbUxaLWZHoRXSNVTRJrv1bssmKeef","type":"DataIntegrityProof","verificationMethod":"did:key:z6MketPC32eF8aZtP5WQcQAexaubxtYoBWshNX2TYrc7yfbg#z6MketPC32eF8aZtP5WQcQAexaubxtYoBWshNX2TYrc7yfbg"}]]
2 changes: 2 additions & 0 deletions testdata/tu001-expect.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
["1-QmQrc5CvaJbEYmCx9r61En9UzCpxbm8QeZAP5QCJdnygxd","2024-09-06T15:53:15+02:00",{"method":"did:tdw:0.3","scid":"QmdhbxVLQbjdb2UatyP19k6YsYLmUcgXhoZY2741vodGdG","updateKeys":["z6MkmNkGhsR2jCWXrguBh3nhVDQY4Ssgx12oD8Qfk8m4q4of"]},{"value":{"@context":["https://www.w3.org/ns/did/v1"],"id":"did:tdw:QmdhbxVLQbjdb2UatyP19k6YsYLmUcgXhoZY2741vodGdG:example.com"}},[{"challenge":"1-QmQrc5CvaJbEYmCx9r61En9UzCpxbm8QeZAP5QCJdnygxd","created":"2024-09-06T15:53:15+02:00","cryptosuite":"eddsa-jcs-2022","proofPurpose":"authentication","proofValue":"z5Pbr7fgrGSeZtGjgLPR4hxdw4Qxb9PVGX7tj3YsocsSKarLJ22H9amf6e5V4SfVt39ypDK2zthiztNcuq15cu1vy","type":"DataIntegrityProof","verificationMethod":"did:key:z6MkmNkGhsR2jCWXrguBh3nhVDQY4Ssgx12oD8Qfk8m4q4of#z6MkmNkGhsR2jCWXrguBh3nhVDQY4Ssgx12oD8Qfk8m4q4of"}]]
["2-QmS5PDRU2G6LYHDppgkeQrehyksFBjCM9ug4oej44UXRVr","2024-09-06T15:53:15+02:00",{},{"patch":[{"op":"add","path":"/service","value":[{"id":"did:tdw:QmdhbxVLQbjdb2UatyP19k6YsYLmUcgXhoZY2741vodGdG:example.com#service-1","serviceEndpoint":"https://example.com/service/1","type":"ExampleService"}]}]},[{"challenge":"2-QmS5PDRU2G6LYHDppgkeQrehyksFBjCM9ug4oej44UXRVr","created":"2024-09-06T15:53:15+02:00","cryptosuite":"eddsa-jcs-2022","proofPurpose":"authentication","proofValue":"zboDPuw1gzyUv9Ve7Kr1cmXgxX4MPAer4JJ9nLxhERmDCoT5115Xq1bZwTcD6KcpVP5j7vszdr7ppFPrdqLzApt6","type":"DataIntegrityProof","verificationMethod":"did:key:z6MkmNkGhsR2jCWXrguBh3nhVDQY4Ssgx12oD8Qfk8m4q4of#z6MkmNkGhsR2jCWXrguBh3nhVDQY4Ssgx12oD8Qfk8m4q4of"}]]
1 change: 1 addition & 0 deletions testdata/tu001-input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["1-QmQrc5CvaJbEYmCx9r61En9UzCpxbm8QeZAP5QCJdnygxd","2024-09-06T15:53:15+02:00",{"method":"did:tdw:0.3","scid":"QmdhbxVLQbjdb2UatyP19k6YsYLmUcgXhoZY2741vodGdG","updateKeys":["z6MkmNkGhsR2jCWXrguBh3nhVDQY4Ssgx12oD8Qfk8m4q4of"]},{"value":{"@context":["https://www.w3.org/ns/did/v1"],"id":"did:tdw:QmdhbxVLQbjdb2UatyP19k6YsYLmUcgXhoZY2741vodGdG:example.com"}},[{"challenge":"1-QmQrc5CvaJbEYmCx9r61En9UzCpxbm8QeZAP5QCJdnygxd","created":"2024-09-06T15:53:15+02:00","cryptosuite":"eddsa-jcs-2022","proofPurpose":"authentication","proofValue":"z5Pbr7fgrGSeZtGjgLPR4hxdw4Qxb9PVGX7tj3YsocsSKarLJ22H9amf6e5V4SfVt39ypDK2zthiztNcuq15cu1vy","type":"DataIntegrityProof","verificationMethod":"did:key:z6MkmNkGhsR2jCWXrguBh3nhVDQY4Ssgx12oD8Qfk8m4q4of#z6MkmNkGhsR2jCWXrguBh3nhVDQY4Ssgx12oD8Qfk8m4q4of"}]]
1 change: 1 addition & 0 deletions testdata/tv002-input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["1-QmRfhbuYqDsewfWL8cQFajYyWpiPj61zBp4kHCCguPaHt9","2024-09-06T15:53:15+02:00",{"method":"did:tdw:0.3","scid":"QmebKNjFvJUHkMfPgty1vjMtkqJ1AeR5xvgiiDS4cqKtHm","updateKeys":["z6Mkh72WZYKL8VUYM79V7JGzod4T2dEj1bLnccQG365rfc9i"]},{"value":{"@context":["https://www.w3.org/ns/did/v1"],"id":"did:tdw:QmebKNjFvJUHkMfPgty1vjMtkqJ1AeR5xvgiiDS4cqKtHm:example.com"}},[{"challenge":"1-QmRfhbuYqDsewfWL8cQFajYyWpiPj61zBp4kHCCguPaHt9","created":"2024-09-06T15:53:15+02:00","cryptosuite":"eddsa-jcs-2022","proofPurpose":"authentication","proofValue":"invalid","type":"DataIntegrityProof","verificationMethod":"did:key:z6Mkh72WZYKL8VUYM79V7JGzod4T2dEj1bLnccQG365rfc9i#z6Mkh72WZYKL8VUYM79V7JGzod4T2dEj1bLnccQG365rfc9i"}]]
Loading