From 066c9d765bdd6111293151414b249aaef75709fc Mon Sep 17 00:00:00 2001 From: Doug Davis Date: Fri, 10 Jan 2025 00:10:23 +0000 Subject: [PATCH] more xref testing Signed-off-by: Doug Davis --- README.md | 3 - registry/group.go | 2 +- registry/httpStuff.go | 2 +- registry/resource.go | 68 ++++-- tests/http2_test.go | 2 +- tests/xref_test.go | 544 +++++++++++++++++++++++++++++++++++++++++- 6 files changed, 588 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 1146f632..9aaa11a0 100644 --- a/README.md +++ b/README.md @@ -142,9 +142,6 @@ TODOs: - support ETag/If-Match - update epoch/modifiedat of parent when nested entity is added/removed - ?nested isn't needed to update `model` or `meta` attributes -- test for PUT with "versions" on an existing resource/xref - should igore - the versions -- test clearing xref and setting defver attrs, via res and via meta directly - test to ensure meta epoch changes as versions are added/removed - test the timestamps in meta. Should change as versions are added/removed. - remove entities from cache upon delete diff --git a/registry/group.go b/registry/group.go index 1b9461b6..ee27856a 100644 --- a/registry/group.go +++ b/registry/group.go @@ -300,7 +300,7 @@ func (g *Group) UpsertResourceWithObject(rType string, id string, vID string, ob // Process the "meta" sub-object if there if !IsNil(metaObj) { - _, _, err := r.UpsertMetaWithObject(metaObj, addType) + _, _, err := r.UpsertMetaWithObject(metaObj, addType, false) if err != nil { if isNew { // Needed if doing local func calls to create the Resource diff --git a/registry/httpStuff.go b/registry/httpStuff.go index f9fe3cc4..f07c6121 100644 --- a/registry/httpStuff.go +++ b/registry/httpStuff.go @@ -1609,7 +1609,7 @@ func HTTPPutPost(info *RequestInfo) error { } // Technically, this will always "update" not "insert" - meta, _, err := resource.UpsertMetaWithObject(IncomingObj, addType) + meta, _, err := resource.UpsertMetaWithObject(IncomingObj, addType, true) if err != nil { info.StatusCode = http.StatusBadRequest return err diff --git a/registry/resource.go b/registry/resource.go index e8d58c67..e7165a4d 100644 --- a/registry/resource.go +++ b/registry/resource.go @@ -382,8 +382,13 @@ func (r *Resource) SetDefault(newDefault *Version) error { return meta.SetSave("defaultversionid", newDefault.UID) } -// *Meta, isNew, error -func (r *Resource) UpsertMetaWithObject(obj Object, addType AddType) (*Meta, bool, error) { +// returns *Meta, isNew, error +// "createVersion" means we should create a version if there isn't already +// one there. This will only happen when the client talks directly to "meta" +// w/o the surrounding Resource object. AND, for now, we only do it when +// we're removing the 'xref' attr. Other cases, the http layer would have +// already create the Resource and default version for us. +func (r *Resource) UpsertMetaWithObject(obj Object, addType AddType, createVersion bool) (*Meta, bool, error) { log.VPrintf(3, ">Enter: UpsertMeta(%s,%v)", r.UID, addType) defer log.VPrintf(3, " newEpoch { newEpoch = targetEpoch } + meta.JustSet("epoch", newEpoch) + meta.JustSet("#epoch", nil) + // We have to fake out the updateFn to think the existing values + // are the # values + meta.EpochSet = false + meta.Object["epoch"] = newEpoch delete(meta.NewObject, "xref") if err = meta.JustSet("xref", nil); err != nil { @@ -537,24 +545,25 @@ func (r *Resource) UpsertMetaWithObject(obj Object, addType AddType) (*Meta, boo meta.JustSet("#nextversionid", 1) } - meta.JustSet("epoch", newEpoch) - meta.JustSet("#epoch", nil) - - // We have to fake out the updateFn to think the existing values - // are the # values - meta.Object["epoch"] = newEpoch - - meta.EpochSet = false - if IsNil(meta.NewObject["createdat"]) { meta.JustSet("createdat", meta.Object["#createdat"]) meta.JustSet("#createdat", nil) meta.Object["createdat"] = meta.Object["#createdat"] } - if IsNil(meta.NewObject["modifiedat"]) { - meta.JustSet("modifiedat", meta.Object["#modifiedat"]) - meta.JustSet("#modifiedat", nil) - meta.Object["modifiedat"] = meta.Object["#modifiedat"] + + // if createVersion is true, make sure we have at least one + // version + if createVersion { + vs, err := r.GetVersionIDs() + if err != nil { + return nil, false, err + } + if len(vs) == 0 { + _, _, err = r.UpsertVersionWithObject("", nil, ADD_ADD) + if err != nil { + return nil, false, err + } + } } /* @@ -566,11 +575,19 @@ func (r *Resource) UpsertMetaWithObject(obj Object, addType AddType) (*Meta, boo */ } else { // Clear all existing attributes except ID - meta.JustSet("#epoch", meta.Object["epoch"]) - meta.JustSet("#createdat", meta.Object["createdat"]) + oldEpoch := meta.Object["epoch"] + if IsNil(oldEpoch) { + oldEpoch = 0 + } + meta.JustSet("#epoch", oldEpoch) + + oldCA := meta.Object["createdat"] + if IsNil(oldCA) { + oldCA = meta.tx.CreateTime + } + meta.JustSet("#createdat", oldCA) + // meta.JustSet("createdat", nil) - meta.JustSet("#modifiedat", meta.Object["modifiedat"]) - // meta.JustSet("modifiedat", nil) extraAttrs := []string{} for k, _ := range meta.NewObject { @@ -622,7 +639,7 @@ func (r *Resource) UpsertMetaWithObject(obj Object, addType AddType) (*Meta, boo // - defaultversionsticky, if there // - defaultversionid, if defaultversionsticky is set - stickyAny := meta.NewObject["defaultversionsticky"] + stickyAny := meta.Get("defaultversionsticky") if !IsNil(stickyAny) && stickyAny != true && stickyAny != false { return nil, false, fmt.Errorf("'defaultversionsticky' must be a " + "boolean or null") @@ -645,7 +662,7 @@ func (r *Resource) UpsertMetaWithObject(obj Object, addType AddType) (*Meta, boo Must(err) if defaultVersionID != "" && IsNil(v) { return nil, false, - fmt.Errorf("Can't find version %q", defaultVersionID) + fmt.Errorf("Version %q not found", defaultVersionID) } meta.JustSet(r.Singular+"id", r.UID) @@ -752,7 +769,6 @@ func (r *Resource) UpsertVersionWithObject(id string, obj Object, addType AddTyp return nil, false, err } } - // Apply properties if obj != nil { // If there's a doc but no "contenttype" value then: diff --git a/tests/http2_test.go b/tests/http2_test.go index 6e7666d8..f92f6b30 100644 --- a/tests/http2_test.go +++ b/tests/http2_test.go @@ -5780,7 +5780,7 @@ func TestHTTPNestedResources(t *testing.T) { }`, Code: 400, ResHeaders: []string{}, - ResBody: `Can't find version "v2" + ResBody: `Version "v2" not found `, }) diff --git a/tests/xref_test.go b/tests/xref_test.go index 0e649e52..4e6218c7 100644 --- a/tests/xref_test.go +++ b/tests/xref_test.go @@ -459,7 +459,7 @@ func TestXrefBasic(t *testing.T) { "xid": "/dirs/d1/files/fx/meta", "epoch": 4, "createdat": "2024-01-01T12:00:01Z", - "modifiedat": "2024-01-01T12:00:04Z", + "modifiedat": "2024-01-01T12:00:03Z", "defaultversionid": "1", "defaultversionurl": "http://localhost:8181/dirs/d1/files/fx/versions/1$structure" @@ -541,3 +541,545 @@ func TestXrefErrors(t *testing.T) { } `) } + +func TestXrefRevert(t *testing.T) { + reg := NewRegistry("TestXrefRevert") + defer PassDeleteReg(t, reg) + + gm, _ := reg.Model.AddGroupModel("dirs", "dir") + gm.AddResourceModel("files", "file", 0, true, true, false) + d, _ := reg.AddGroup("dirs", "d1") + + xHTTP(t, reg, "PUT", "/dirs/d1/files/f1/versions/v9", + `{"description":"hi"}`, 201, `{ + "fileid": "f1", + "versionid": "v9", + "self": "http://localhost:8181/dirs/d1/files/f1/versions/v9", + "xid": "/dirs/d1/files/f1/versions/v9", + "epoch": 1, + "isdefault": true, + "description": "hi", + "createdat": "2025-01-09T15:59:29.22249886Z", + "modifiedat": "2025-01-09T15:59:29.22249886Z" +} +`) + + // Revert with no versions (create 2 files so we can grab the TS from f0) + //////////////////////////////////////////////////////// + xHTTP(t, reg, "POST", "/dirs/d1/files/?inline=meta", + `{"f0":{}, "fx":{"meta":{"xref":"/dirs/d1/files/f1"}}}`, 200, `{ + "f0": { + "fileid": "f0", + "versionid": "1", + "self": "http://localhost:8181/dirs/d1/files/f0", + "xid": "/dirs/d1/files/f0", + "epoch": 1, + "isdefault": true, + "createdat": "YYYY-MM-DDTHH:MM:01Z", + "modifiedat": "YYYY-MM-DDTHH:MM:01Z", + + "metaurl": "http://localhost:8181/dirs/d1/files/f0/meta", + "meta": { + "fileid": "f0", + "self": "http://localhost:8181/dirs/d1/files/f0/meta", + "xid": "/dirs/d1/files/f0/meta", + "epoch": 1, + "createdat": "YYYY-MM-DDTHH:MM:01Z", + "modifiedat": "YYYY-MM-DDTHH:MM:01Z", + + "defaultversionid": "1", + "defaultversionurl": "http://localhost:8181/dirs/d1/files/f0/versions/1" + }, + "versionsurl": "http://localhost:8181/dirs/d1/files/f0/versions", + "versionscount": 1 + }, + "fx": { + "fileid": "fx", + "versionid": "v9", + "self": "http://localhost:8181/dirs/d1/files/fx", + "xid": "/dirs/d1/files/fx", + "epoch": 1, + "isdefault": true, + "description": "hi", + "createdat": "YYYY-MM-DDTHH:MM:02Z", + "modifiedat": "YYYY-MM-DDTHH:MM:02Z", + + "metaurl": "http://localhost:8181/dirs/d1/files/fx/meta", + "meta": { + "fileid": "fx", + "self": "http://localhost:8181/dirs/d1/files/fx/meta", + "xid": "/dirs/d1/files/fx/meta", + "xref": "/dirs/d1/files/f1", + "epoch": 1, + "createdat": "YYYY-MM-DDTHH:MM:02Z", + "modifiedat": "YYYY-MM-DDTHH:MM:02Z", + + "defaultversionid": "v9", + "defaultversionurl": "http://localhost:8181/dirs/d1/files/fx/versions/v9" + }, + "versionsurl": "http://localhost:8181/dirs/d1/files/fx/versions", + "versionscount": 1 + } +} +`) + + // Grab F0's timestamp so we can compare later + f0, err := d.FindResource("files", "f0", false) + xNoErr(t, err) + f0TS := f0.Get("createdat").(string) + xCheck(t, f0TS > "2024", "bad ts: %s", f0TS) + + // Notice epoch will be 2 not 1 since it's max(0,fx.epoch)+1 + // Notice meta.createat == f0's createdat, others are now() + xHTTP(t, reg, "PUT", "/dirs/d1/files/fx?inline=meta", `{ + "meta":{"xref":null} +} `, 200, `{ + "fileid": "fx", + "versionid": "1", + "self": "http://localhost:8181/dirs/d1/files/fx", + "xid": "/dirs/d1/files/fx", + "epoch": 1, + "isdefault": true, + "createdat": "2025-01-01T12:00:02Z", + "modifiedat": "2025-01-01T12:00:02Z", + + "metaurl": "http://localhost:8181/dirs/d1/files/fx/meta", + "meta": { + "fileid": "fx", + "self": "http://localhost:8181/dirs/d1/files/fx/meta", + "xid": "/dirs/d1/files/fx/meta", + "epoch": 2, + "createdat": "2025-01-01T12:00:01Z", + "modifiedat": "2025-01-01T12:00:02Z", + + "defaultversionid": "1", + "defaultversionurl": "http://localhost:8181/dirs/d1/files/fx/versions/1" + }, + "versionsurl": "http://localhost:8181/dirs/d1/files/fx/versions", + "versionscount": 1 +} +`) + fx, err := d.FindResource("files", "fx", false) + xNoErr(t, err) + fxMeta, err := fx.FindMeta(false) + xNoErr(t, err) + fxMetaTS := fxMeta.Get("createdat").(string) + xCheck(t, f0TS == fxMetaTS, "Bad ts: %s/%s", f0TS, fxMetaTS) + + // Revert with empty versions + //////////////////////////////////////////////////////// + xHTTP(t, reg, "PUT", "/dirs/d1/files/fx?inline=meta", + `{"meta":{"xref":"/dirs/d1/files/f1"}}`, 200, `{ + "fileid": "fx", + "versionid": "v9", + "self": "http://localhost:8181/dirs/d1/files/fx", + "xid": "/dirs/d1/files/fx", + "epoch": 1, + "isdefault": true, + "description": "hi", + "createdat": "2025-01-01T12:00:00Z", + "modifiedat": "2025-01-01T12:00:00Z", + + "metaurl": "http://localhost:8181/dirs/d1/files/fx/meta", + "meta": { + "fileid": "fx", + "self": "http://localhost:8181/dirs/d1/files/fx/meta", + "xid": "/dirs/d1/files/fx/meta", + "xref": "/dirs/d1/files/f1", + "epoch": 1, + "createdat": "2025-01-01T12:00:00Z", + "modifiedat": "2025-01-01T12:00:00Z", + + "defaultversionid": "v9", + "defaultversionurl": "http://localhost:8181/dirs/d1/files/fx/versions/v9" + }, + "versionsurl": "http://localhost:8181/dirs/d1/files/fx/versions", + "versionscount": 1 +} +`) + + xHTTP(t, reg, "PUT", "/dirs/d1/files/fx?inline=meta", `{ + "meta":{"xref":null}, + "versions": {} +} `, 200, `{ + "fileid": "fx", + "versionid": "1", + "self": "http://localhost:8181/dirs/d1/files/fx", + "xid": "/dirs/d1/files/fx", + "epoch": 1, + "isdefault": true, + "createdat": "2025-01-01T12:00:02Z", + "modifiedat": "2025-01-01T12:00:02Z", + + "metaurl": "http://localhost:8181/dirs/d1/files/fx/meta", + "meta": { + "fileid": "fx", + "self": "http://localhost:8181/dirs/d1/files/fx/meta", + "xid": "/dirs/d1/files/fx/meta", + "epoch": 3, + "createdat": "2025-01-01T12:00:01Z", + "modifiedat": "2025-01-01T12:00:02Z", + + "defaultversionid": "1", + "defaultversionurl": "http://localhost:8181/dirs/d1/files/fx/versions/1" + }, + "versionsurl": "http://localhost:8181/dirs/d1/files/fx/versions", + "versionscount": 1 +} +`) + xNoErr(t, fxMeta.Refresh()) + xNoErr(t, fx.Refresh()) + xCheckEqual(t, "ts check", f0TS, fxMeta.Get("createdat").(string)) + xCheckGreater(t, "ts check", fx.Get("createdat").(string), f0TS) + + // Revert with one version + //////////////////////////////////////////////////////// + xHTTP(t, reg, "PUT", "/dirs/d1/files/fx?inline=meta", + `{"meta":{"xref":"/dirs/d1/files/f1"}}`, 200, `{ + "fileid": "fx", + "versionid": "v9", + "self": "http://localhost:8181/dirs/d1/files/fx", + "xid": "/dirs/d1/files/fx", + "epoch": 1, + "isdefault": true, + "description": "hi", + "createdat": "2025-01-01T12:00:00Z", + "modifiedat": "2025-01-01T12:00:00Z", + + "metaurl": "http://localhost:8181/dirs/d1/files/fx/meta", + "meta": { + "fileid": "fx", + "self": "http://localhost:8181/dirs/d1/files/fx/meta", + "xid": "/dirs/d1/files/fx/meta", + "xref": "/dirs/d1/files/f1", + "epoch": 1, + "createdat": "2025-01-01T12:00:00Z", + "modifiedat": "2025-01-01T12:00:00Z", + + "defaultversionid": "v9", + "defaultversionurl": "http://localhost:8181/dirs/d1/files/fx/versions/v9" + }, + "versionsurl": "http://localhost:8181/dirs/d1/files/fx/versions", + "versionscount": 1 +} +`) + + xHTTP(t, reg, "PUT", "/dirs/d1/files/fx?inline=meta&nested", `{ + "meta":{"xref":null}, + "versions": { "v1": { "description": "ver1" } } +} `, 200, `{ + "fileid": "fx", + "versionid": "v1", + "self": "http://localhost:8181/dirs/d1/files/fx", + "xid": "/dirs/d1/files/fx", + "epoch": 1, + "isdefault": true, + "description": "ver1", + "createdat": "2025-01-01T12:00:01Z", + "modifiedat": "2025-01-01T12:00:01Z", + + "metaurl": "http://localhost:8181/dirs/d1/files/fx/meta", + "meta": { + "fileid": "fx", + "self": "http://localhost:8181/dirs/d1/files/fx/meta", + "xid": "/dirs/d1/files/fx/meta", + "epoch": 4, + "createdat": "2025-01-01T12:00:00Z", + "modifiedat": "2025-01-01T12:00:01Z", + + "defaultversionid": "v1", + "defaultversionurl": "http://localhost:8181/dirs/d1/files/fx/versions/v1" + }, + "versionsurl": "http://localhost:8181/dirs/d1/files/fx/versions", + "versionscount": 1 +} +`) + + xNoErr(t, fxMeta.Refresh()) + xNoErr(t, fx.Refresh()) + xCheckEqual(t, "ts check", f0TS, fxMeta.Get("createdat").(string)) + xCheckGreater(t, "ts check", fx.Get("createdat").(string), f0TS) + + // Revert with two versions - no default + //////////////////////////////////////////////////////// + xHTTP(t, reg, "PUT", "/dirs/d1/files/fx?inline=meta", + `{"meta":{"xref":"/dirs/d1/files/f1"}}`, 200, `{ + "fileid": "fx", + "versionid": "v9", + "self": "http://localhost:8181/dirs/d1/files/fx", + "xid": "/dirs/d1/files/fx", + "epoch": 1, + "isdefault": true, + "description": "hi", + "createdat": "2025-01-01T12:00:00Z", + "modifiedat": "2025-01-01T12:00:00Z", + + "metaurl": "http://localhost:8181/dirs/d1/files/fx/meta", + "meta": { + "fileid": "fx", + "self": "http://localhost:8181/dirs/d1/files/fx/meta", + "xid": "/dirs/d1/files/fx/meta", + "xref": "/dirs/d1/files/f1", + "epoch": 1, + "createdat": "2025-01-01T12:00:00Z", + "modifiedat": "2025-01-01T12:00:00Z", + + "defaultversionid": "v9", + "defaultversionurl": "http://localhost:8181/dirs/d1/files/fx/versions/v9" + }, + "versionsurl": "http://localhost:8181/dirs/d1/files/fx/versions", + "versionscount": 1 +} +`) + + xHTTP(t, reg, "PUT", "/dirs/d1/files/fx?inline=meta&nested", `{ + "meta":{"xref":null}, + "versions": { "z1": {}, "a1": {} } +} `, 200, `{ + "fileid": "fx", + "versionid": "z1", + "self": "http://localhost:8181/dirs/d1/files/fx", + "xid": "/dirs/d1/files/fx", + "epoch": 1, + "isdefault": true, + "createdat": "YYYY-MM-DDTHH:MM:01Z", + "modifiedat": "YYYY-MM-DDTHH:MM:01Z", + + "metaurl": "http://localhost:8181/dirs/d1/files/fx/meta", + "meta": { + "fileid": "fx", + "self": "http://localhost:8181/dirs/d1/files/fx/meta", + "xid": "/dirs/d1/files/fx/meta", + "epoch": 5, + "createdat": "YYYY-MM-DDTHH:MM:02Z", + "modifiedat": "YYYY-MM-DDTHH:MM:01Z", + + "defaultversionid": "z1", + "defaultversionurl": "http://localhost:8181/dirs/d1/files/fx/versions/z1" + }, + "versionsurl": "http://localhost:8181/dirs/d1/files/fx/versions", + "versionscount": 2 +} +`) + + xNoErr(t, fxMeta.Refresh()) + xNoErr(t, fx.Refresh()) + xCheckEqual(t, "ts check", f0TS, fxMeta.Get("createdat").(string)) + xCheckGreater(t, "ts check", fx.Get("createdat").(string), f0TS) + + // Revert with two versions - w/default query param + //////////////////////////////////////////////////////// + xHTTP(t, reg, "PUT", "/dirs/d1/files/fx?inline=meta", + `{"meta":{"xref":"/dirs/d1/files/f1"}}`, 200, `{ + "fileid": "fx", + "versionid": "v9", + "self": "http://localhost:8181/dirs/d1/files/fx", + "xid": "/dirs/d1/files/fx", + "epoch": 1, + "isdefault": true, + "description": "hi", + "createdat": "2025-01-01T12:00:00Z", + "modifiedat": "2025-01-01T12:00:00Z", + + "metaurl": "http://localhost:8181/dirs/d1/files/fx/meta", + "meta": { + "fileid": "fx", + "self": "http://localhost:8181/dirs/d1/files/fx/meta", + "xid": "/dirs/d1/files/fx/meta", + "xref": "/dirs/d1/files/f1", + "epoch": 1, + "createdat": "2025-01-01T12:00:00Z", + "modifiedat": "2025-01-01T12:00:00Z", + + "defaultversionid": "v9", + "defaultversionurl": "http://localhost:8181/dirs/d1/files/fx/versions/v9" + }, + "versionsurl": "http://localhost:8181/dirs/d1/files/fx/versions", + "versionscount": 1 +} +`) + + xHTTP(t, reg, "PUT", "/dirs/d1/files/fx?inline=meta&nested&setdefaultversionid=bb", `{ + "meta":{"xref":null }, + "versions": { "z2": {}, "b3": {} } +} `, 400, `Version "bb" not found +`) + + xHTTP(t, reg, "PUT", "/dirs/d1/files/fx?inline=meta&nested&setdefaultversionid=b3", `{ + "meta":{"xref":null }, + "versions": { "z2": {}, "b3": {} } +} `, 200, `{ + "fileid": "fx", + "versionid": "b3", + "self": "http://localhost:8181/dirs/d1/files/fx", + "xid": "/dirs/d1/files/fx", + "epoch": 1, + "isdefault": true, + "createdat": "2025-01-01T12:00:02Z", + "modifiedat": "2025-01-01T12:00:02Z", + + "metaurl": "http://localhost:8181/dirs/d1/files/fx/meta", + "meta": { + "fileid": "fx", + "self": "http://localhost:8181/dirs/d1/files/fx/meta", + "xid": "/dirs/d1/files/fx/meta", + "epoch": 6, + "createdat": "2025-01-01T12:00:01Z", + "modifiedat": "2025-01-01T12:00:02Z", + + "defaultversionid": "b3", + "defaultversionurl": "http://localhost:8181/dirs/d1/files/fx/versions/b3", + "defaultversionsticky": true + }, + "versionsurl": "http://localhost:8181/dirs/d1/files/fx/versions", + "versionscount": 2 +} +`) + + xNoErr(t, fxMeta.Refresh()) + xNoErr(t, fx.Refresh()) + xCheckEqual(t, "ts check", f0TS, fxMeta.Get("createdat").(string)) + xCheckGreater(t, "ts check", fx.Get("createdat").(string), f0TS) + + // Revert with two versions - w/default in meta + //////////////////////////////////////////////////////// + xHTTP(t, reg, "PUT", "/dirs/d1/files/fx?inline=meta", + `{"meta":{"xref":"/dirs/d1/files/f1"}}`, 200, `{ + "fileid": "fx", + "versionid": "v9", + "self": "http://localhost:8181/dirs/d1/files/fx", + "xid": "/dirs/d1/files/fx", + "epoch": 1, + "isdefault": true, + "description": "hi", + "createdat": "2025-01-01T12:00:00Z", + "modifiedat": "2025-01-01T12:00:00Z", + + "metaurl": "http://localhost:8181/dirs/d1/files/fx/meta", + "meta": { + "fileid": "fx", + "self": "http://localhost:8181/dirs/d1/files/fx/meta", + "xid": "/dirs/d1/files/fx/meta", + "xref": "/dirs/d1/files/f1", + "epoch": 1, + "createdat": "2025-01-01T12:00:00Z", + "modifiedat": "2025-01-01T12:00:00Z", + + "defaultversionid": "v9", + "defaultversionurl": "http://localhost:8181/dirs/d1/files/fx/versions/v9" + }, + "versionsurl": "http://localhost:8181/dirs/d1/files/fx/versions", + "versionscount": 1 +} +`) + + xHTTP(t, reg, "PUT", "/dirs/d1/files/fx?inline=meta&nested", `{ + "meta":{"xref":null, + "defaultversionid": "bb", + "defaultversionsticky": true }, + "versions": { "z2": {}, "b3": {} } +} `, 400, `Version "bb" not found +`) + + xHTTP(t, reg, "PUT", "/dirs/d1/files/fx?inline=meta&nested", `{ + "meta":{"xref":null, + "defaultversionid": "b3", + "defaultversionsticky": true }, + "versions": { "z2": {}, "b3": {} } +} `, 200, `{ + "fileid": "fx", + "versionid": "b3", + "self": "http://localhost:8181/dirs/d1/files/fx", + "xid": "/dirs/d1/files/fx", + "epoch": 1, + "isdefault": true, + "createdat": "2025-01-01T12:00:02Z", + "modifiedat": "2025-01-01T12:00:02Z", + + "metaurl": "http://localhost:8181/dirs/d1/files/fx/meta", + "meta": { + "fileid": "fx", + "self": "http://localhost:8181/dirs/d1/files/fx/meta", + "xid": "/dirs/d1/files/fx/meta", + "epoch": 7, + "createdat": "2025-01-01T12:00:01Z", + "modifiedat": "2025-01-01T12:00:02Z", + + "defaultversionid": "b3", + "defaultversionurl": "http://localhost:8181/dirs/d1/files/fx/versions/b3", + "defaultversionsticky": true + }, + "versionsurl": "http://localhost:8181/dirs/d1/files/fx/versions", + "versionscount": 2 +} +`) + xNoErr(t, fxMeta.Refresh()) + xNoErr(t, fx.Refresh()) + xCheckEqual(t, "ts check", f0TS, fxMeta.Get("createdat").(string)) + xCheckGreater(t, "ts check", fx.Get("createdat").(string), f0TS) + + // Revert via meta + default + //////////////////////////////////////////////////////// + xHTTP(t, reg, "PUT", "/dirs/d1/files/fx/meta", + `{"xref":"/dirs/d1/files/f1"}`, 200, `{ + "fileid": "fx", + "self": "http://localhost:8181/dirs/d1/files/fx/meta", + "xid": "/dirs/d1/files/fx/meta", + "xref": "/dirs/d1/files/f1", + "epoch": 1, + "createdat": "2025-01-01T12:00:00Z", + "modifiedat": "2025-01-01T12:00:00Z", + + "defaultversionid": "v9", + "defaultversionurl": "http://localhost:8181/dirs/d1/files/fx/versions/v9" +} +`) + + // defaultversionid is ignored + xHTTP(t, reg, "PUT", "/dirs/d1/files/fx/meta", + `{"xref":null, + "defaultversionid": "bb"}`, 200, `{ + "fileid": "fx", + "self": "http://localhost:8181/dirs/d1/files/fx/meta", + "xid": "/dirs/d1/files/fx/meta", + "epoch": 8, + "createdat": "2025-01-09T23:31:06.391225888Z", + "modifiedat": "2025-01-09T23:31:07.033435714Z", + + "defaultversionid": "1", + "defaultversionurl": "http://localhost:8181/dirs/d1/files/fx/versions/1" +} +`) + + // reset again + xHTTP(t, reg, "PUT", "/dirs/d1/files/fx/meta", + `{"xref":"/dirs/d1/files/f1"}`, 200, `*`) + + xHTTP(t, reg, "PUT", "/dirs/d1/files/fx/meta", + `{"xref":null, + "defaultversionid": "bb", + "defaultversionsticky": true}`, 400, `Version "bb" not found +`) + + xHTTP(t, reg, "PUT", "/dirs/d1/files/fx/meta", + `{"xref":null, + "defaultversionsticky": true}`, 200, `{ + "fileid": "fx", + "self": "http://localhost:8181/dirs/d1/files/fx/meta", + "xid": "/dirs/d1/files/fx/meta", + "epoch": 9, + "createdat": "2025-01-09T23:16:04.619269627Z", + "modifiedat": "2025-01-09T23:16:05.273949318Z", + + "defaultversionid": "1", + "defaultversionurl": "http://localhost:8181/dirs/d1/files/fx/versions/1", + "defaultversionsticky": true +} +`) + + xNoErr(t, fxMeta.Refresh()) + xNoErr(t, fx.Refresh()) + xCheckEqual(t, "ts check", f0TS, fxMeta.Get("createdat").(string)) + xCheckGreater(t, "ts check", fx.Get("createdat").(string), f0TS) + +}