diff --git a/README.md b/README.md
index 3cbaa3b..db84b4c 100644
--- a/README.md
+++ b/README.md
@@ -158,6 +158,7 @@ Sample skips:
| persisted | The updated resource should be persisted and reachable with Get. | Generated only if all are true:
- has Update method
- Update method does not return long-running operation
- has Get method
|
| preserve create_time | The field create_time should be preserved when a '\*'-update mask is used. | Generated only if all are true: - has Update method
- Update method does not return long-running operation
- has field 'create_time'
- resource has any required fields
|
| etag mismatch | Method should fail with Aborted if the supplied etag doesnt match the current etag value. | Generated only if all are true: - has Update method
- request has etag field
- has field 'etag'
|
+| etag updated | Field etag should have a new value when the resource is successfully updated. | Generated only if all are true: - has Update method
- request has etag field
- has field 'etag'
|
| not found | Method should fail with NotFound if the resource does not exist. | Generated only if all are true: |
| invalid update mask | The method should fail with InvalidArgument if the update_mask is invalid. | Generated only if all are true: - has Update method
- Update method has update_mask
|
| required fields | Method should fail with InvalidArgument if any required field is missing when called with '\*' update_mask. | Generated only if all are true: - has Update method
- resource has any required fields
|
diff --git a/internal/aiptest/update/etag.go b/internal/aiptest/update/etag.go
index 2c9e7b5..a0fef71 100644
--- a/internal/aiptest/update/etag.go
+++ b/internal/aiptest/update/etag.go
@@ -39,3 +39,35 @@ var etagMismatch = suite.Test{
return nil
},
}
+
+//nolint:gochecknoglobals
+var etagUpdated = suite.Test{
+ Name: "etag updated",
+ Doc: []string{
+ "Field etag should have a new value when the resource is successfully updated.",
+ },
+ OnlyIf: suite.OnlyIfs(
+ onlyif.HasMethod(aipreflect.MethodTypeUpdate),
+ onlyif.HasRequestEtag(aipreflect.MethodTypeUpdate),
+ onlyif.HasField("etag"),
+ ),
+ Generate: func(f *protogen.GeneratedFile, scope suite.Scope) error {
+ if util.HasParent(scope.Resource) {
+ f.P("parent := ", ident.FixtureNextParent, "(t, false)")
+ f.P("created := fx.create(t, parent)")
+ } else {
+ f.P("created := fx.create(t)")
+ }
+ updateMethod, _ := util.StandardMethod(scope.Service, scope.Resource, aipreflect.MethodTypeUpdate)
+ util.MethodUpdate{
+ Resource: scope.Resource,
+ Method: updateMethod,
+ Parent: "parent",
+ Name: "created.Name",
+ Etag: "created.Etag",
+ }.Generate(f, "updated", "err", ":=")
+ f.P(ident.AssertNilError, "(t, err)")
+ f.P(ident.AssertCheck, "(t, updated.Etag != created.Etag)")
+ return nil
+ },
+}
diff --git a/internal/aiptest/update/update.go b/internal/aiptest/update/update.go
index a311478..c7aceac 100644
--- a/internal/aiptest/update/update.go
+++ b/internal/aiptest/update/update.go
@@ -19,6 +19,7 @@ var Suite = suite.Suite{
persisted,
preserveCreateTime,
etagMismatch,
+ etagUpdated,
},
TestGroups: []suite.TestGroup{
withResourceGroup,
@@ -41,6 +42,5 @@ var withResourceGroup = suite.TestGroup{
invalidUpdateMask,
requiredFields,
// TODO: add test for supplying wildcard as name
- // TODO: add test for etags
},
}
diff --git a/proto/gen/einride/example/freight/v1/freight_service_aiptest.pb.go b/proto/gen/einride/example/freight/v1/freight_service_aiptest.pb.go
index 0cee5c1..79f6804 100644
--- a/proto/gen/einride/example/freight/v1/freight_service_aiptest.pb.go
+++ b/proto/gen/einride/example/freight/v1/freight_service_aiptest.pb.go
@@ -349,6 +349,20 @@ func (fx *FreightServiceShipperTestSuiteConfig) testUpdate(t *testing.T) {
assert.Equal(t, codes.Aborted, status.Code(err), err)
})
+ // Field etag should have a new value when the resource is successfully updated.
+ t.Run("etag updated", func(t *testing.T) {
+ fx.maybeSkip(t)
+ created := fx.create(t)
+ msg := fx.Update()
+ msg.Name = created.Name
+ updated, err := fx.service.UpdateShipper(fx.ctx, &UpdateShipperRequest{
+ Shipper: msg,
+ Etag: created.Etag,
+ })
+ assert.NilError(t, err)
+ assert.Check(t, updated.Etag != created.Etag)
+ })
+
created := fx.create(t)
// Method should fail with NotFound if the resource does not exist.
t.Run("not found", func(t *testing.T) {
@@ -993,6 +1007,21 @@ func (fx *FreightServiceSiteTestSuiteConfig) testUpdate(t *testing.T) {
assert.Equal(t, codes.Aborted, status.Code(err), err)
})
+ // Field etag should have a new value when the resource is successfully updated.
+ t.Run("etag updated", func(t *testing.T) {
+ fx.maybeSkip(t)
+ parent := fx.nextParent(t, false)
+ created := fx.create(t, parent)
+ msg := fx.Update(parent)
+ msg.Name = created.Name
+ updated, err := fx.service.UpdateSite(fx.ctx, &UpdateSiteRequest{
+ Site: msg,
+ Etag: created.Etag,
+ })
+ assert.NilError(t, err)
+ assert.Check(t, updated.Etag != created.Etag)
+ })
+
parent := fx.nextParent(t, false)
created := fx.create(t, parent)
// Method should fail with NotFound if the resource does not exist.