Repository: googleapis/api-linter
Pull request #284 (2019-11-02 18:26:42)

[AIP-134] Document AIP-134 rules.
Also fixes some errata I found when copying and pasting.


Patch:
diff --git a/docs/rules/core/0133-http-uri-parent.md b/docs/rules/core/0133-http-uri-parent.md
index 62e5021..fc48637 100644
--- a/docs/rules/core/0133-http-uri-parent.md
+++ b/docs/rules/core/0133-http-uri-parent.md
@@ -7,8 +7,8 @@ rule:
 
 # Create methods: HTTP URI parent field
 
-This rule enforces that all `Create` RPCs contain the `parent` field to the HTTP URI,
-as mandated in [AIP-133][].
+This rule enforces that all `Create` RPCs map the `parent` field to the HTTP
+URI, as mandated in [AIP-133][].
 
 ## Details
 
diff --git a/docs/rules/core/0133-request-message-name.md b/docs/rules/core/0133-request-message-name.md
index d6cc738..ce85142 100644
--- a/docs/rules/core/0133-request-message-name.md
+++ b/docs/rules/core/0133-request-message-name.md
@@ -7,14 +7,14 @@ rule:
 
 # Create methods: Request message
 
-This rule enforces that all `Get` RPCs have a request message name of
-`Get*Request`, as mandated in [AIP-133][].
+This rule enforces that all `Create` RPCs have a request message name of
+`Create*Request`, as mandated in [AIP-133][].
 
 ## Details
 
-This rule looks at any message matching beginning with `Get`, and complains if
-the name of the corresponding input message does not match the name of the RPC
-with the suffix `Request` appended.
+This rule looks at any message matching beginning with `Create`, and complains
+if the name of the corresponding input message does not match the name of the
+RPC with the suffix `Request` appended.
 
 ## Examples
 
@@ -22,11 +22,10 @@ with the suffix `Request` appended.
 
 ```proto
 // Incorrect.
-// Should be `CreateBookRequest`.
-rpc CreateBook(CreateBookReq) returns (Book) {
+rpc CreateBook(Book) returns (Book) {  // Should be `CreateBookRequest`.
   option (google.api.http) = {
     post: "/v1/{parent=publishers/*}/books"
-    body: "book"
+    body: "*"
   };
 }
 ```
@@ -51,10 +50,10 @@ Remember to also include an [aip.dev/not-precedent][] comment explaining why.
 ```proto
 // (-- api-linter: core::0133::request-message-name=disabled
 //     aip.dev/not-precedent: We need to do this because reasons. --)
-rpc CreateBook(CreateBookReq) returns (Book) {
+rpc CreateBook(Book) returns (Book) {
   option (google.api.http) = {
     post: "/v1/{parent=publishers/*}/books"
-    body: "book"
+    body: "*"
   };
 }
 ```
diff --git a/docs/rules/core/0133-request-resource-field.md b/docs/rules/core/0133-request-resource-field.md
index 5f0d552..565fe8e 100644
--- a/docs/rules/core/0133-request-resource-field.md
+++ b/docs/rules/core/0133-request-resource-field.md
@@ -8,8 +8,7 @@ rule:
 # Create methods: Resource field
 
 This rule enforces that all `Create` standard methods have a field in the
-request message for the resource itself, as mandated in
-[AIP-133](http://aip.dev/133).
+request message for the resource itself, as mandated in [AIP-133][].
 
 ## Details
 
@@ -31,7 +30,7 @@ message CreateBookRequest {
 
 ```proto
 // Incorrect.
-message GetBookRequest {
+message CreateBookRequest {
   bytes parent = 1;
   Book payload = 2;  // Field name should be `book`.
   string book_id = 3;
@@ -52,7 +51,7 @@ message CreateBookRequest {
 ## Disabling
 
 If you need to violate this rule, use a leading comment above the message (if
-the `name` field is missing) or above the field (if it is the wrong type).
+the resource field is missing) or above the field (if it is improperly named).
 Remember to also include an [aip.dev/not-precedent][] comment explaining why.
 
 ```proto
@@ -68,4 +67,5 @@ message CreateBookRequest {
 If you need to violate this rule for an entire file, place the comment at the
 top of the file.
 
+[aip-133]: https://aip.dev/133
 [aip.dev/not-precedent]: https://aip.dev/not-precedent
diff --git a/docs/rules/core/0133-response-message-name.md b/docs/rules/core/0133-response-message-name.md
index 1f5e174..2ce3c88 100644
--- a/docs/rules/core/0133-response-message-name.md
+++ b/docs/rules/core/0133-response-message-name.md
@@ -16,8 +16,15 @@ This rule looks at any message matching beginning with `Create`, and complains
 if the name of the corresponding output message does not match the name of the
 RPC with the prefix `Create` removed.
 
+It also permits a response of `google.longrunning.Operation`; in this case, it
+checks the `response_type` in the `google.longrunning.operation_info`
+annotation and ensures that _it_ corresponds to the name of the RPC with the
+prefix `Create` removed.
+
 ## Examples
 
+### Standard
+
 **Incorrect** code for this rule:
 
 ```proto
@@ -43,6 +50,38 @@ rpc CreateBook(CreateBookRequest) returns (Book) {
 }
 ```
 
+### Long-running operation
+
+```proto
+// Incorrect.
+rpc CreateBook(CreateBookRequest) returns (google.longrunning.Operation) {
+  option (google.api.http) = {
+    post: "/v1/{book.name=publishers/*}/books"
+    body: "book"
+  };
+  option (google.longrunning.operation_info) = {
+    response_type: "CreateBookResponse"  // Should be "Book".
+    metadata_type: "CreateBookMetadata"
+  }
+}
+```
+
+**Correct** code for this rule:
+
+```proto
+// Correct.
+rpc CreateBook(CreateBookRequest) returns (google.longrunning.Operation) {
+  option (google.api.http) = {
+    post: "/v1/{book.name=publishers/*}/books"
+    body: "book"
+  };
+  option (google.longrunning.operation_info) = {
+    response_type: "Book"
+    metadata_type: "CreateBookMetadata"
+  }
+}
+```
+
 ## Disabling
 
 If you need to violate this rule, use a leading comment above the method.
diff --git a/docs/rules/core/0134-http-body.md b/docs/rules/core/0134-http-body.md
new file mode 100644
index 0000000..d85aee6
--- /dev/null
+++ b/docs/rules/core/0134-http-body.md
@@ -0,0 +1,64 @@
+---
+rule:
+  aip: 134
+  name: [core, '0134', http-body]
+  summary: Update methods must have the HTTP body set to the resource.
+---
+
+# Update methods: HTTP body
+
+This rule enforces that all `Update` RPCs set the HTTP `body` to the resource,
+as mandated in [AIP-134][].
+
+## Details
+
+This rule looks at any method whose name begins with `Update`, and complains if
+the HTTP `body` field is not set to the resource being created.
+
+## Examples
+
+**Incorrect** code for this rule:
+
+```proto
+// Incorrect.
+rpc UpdateBook(UpdateBookRequest) returns (Book) {
+  option (google.api.http) = {
+    patch: "/v1/{book.name=publishers/*/books/*}"
+    body: "*"  // This should be "book".
+  };
+}
+```
+
+**Correct** code for this rule:
+
+```proto
+// Correct.
+rpc UpdateBook(UpdateBookRequest) returns (Book) {
+  option (google.api.http) = {
+    patch: "/v1/{book.name=publishers/*/books/*}"
+    body: "book"
+  };
+}
+```
+
+## Disabling
+
+If you need to violate this rule, use a leading comment above the method.
+Remember to also include an [aip.dev/not-precedent][] comment explaining why.
+
+```proto
+// (-- api-linter: core::0134::http-body=disabled
+//     aip.dev/not-precedent: We need to do this because reasons. --)
+rpc UpdateBook(UpdateBookRequest) returns (Book) {
+  option (google.api.http) = {
+    patch: "/v1/{book.name=publishers/*/books/*}"
+    body: "*"
+  };
+}
+```
+
+If you need to violate this rule for an entire file, place the comment at the
+top of the file.
+
+[aip-134]: https://aip.dev/134
+[aip.dev/not-precedent]: https://aip.dev/not-precedent
diff --git a/docs/rules/core/0134-http-method.md b/docs/rules/core/0134-http-method.md
new file mode 100644
index 0000000..652b8ea
--- /dev/null
+++ b/docs/rules/core/0134-http-method.md
@@ -0,0 +1,65 @@
+---
+rule:
+  aip: 134
+  name: [core, '0134', http-method]
+  summary: Update methods must use the PATCH HTTP verb.
+---
+
+# Update methods: PATCH HTTP verb
+
+This rule enforces that all `Update` RPCs use the `PATCH` HTTP verb, as
+mandated in [AIP-134][].
+
+## Details
+
+This rule looks at any message matching beginning with `Update`, and complains
+if the HTTP verb is anything other than `PATCH`. It _does_ check additional
+bindings if they are present.
+
+## Examples
+
+**Incorrect** code for this rule:
+
+```proto
+// Incorrect.
+rpc UpdateBook(UpdateBookRequest) returns (Book) {
+  option (google.api.http) = {
+    put: "/v1/{book.name=publishers/*/books/*}"  // Should be `patch:`.
+    body: "book"
+  };
+}
+```
+
+**Correct** code for this rule:
+
+```proto
+// Correct.
+rpc UpdateBook(UpdateBookRequest) returns (Book) {
+  option (google.api.http) = {
+    patch: "/v1/{book.name=publishers/*/books/*}"
+    body: "book"
+  };
+}
+```
+
+## Disabling
+
+If you need to violate this rule, use a leading comment above the method.
+Remember to also include an [aip.dev/not-precedent][] comment explaining why.
+
+```proto
+// (-- api-linter: core::0134::http-method=disabled
+//     aip.dev/not-precedent: We need to do this because reasons. --)
+rpc UpdateBook(UpdateBookRequest) returns (Book) {
+  option (google.api.http) = {
+    put: "/v1/{book.name=publishers/*/books/*}"  // Should be `patch:`.
+    body: "book"
+  };
+}
+```
+
+If you need to violate this rule for an entire file, place the comment at the
+top of the file.
+
+[aip-134]: https://aip.dev/134
+[aip.dev/not-precedent]: https://aip.dev/not-precedent
diff --git a/docs/rules/core/0134-http-uri-name.md b/docs/rules/core/0134-http-uri-name.md
new file mode 100644
index 0000000..58ab938
--- /dev/null
+++ b/docs/rules/core/0134-http-uri-name.md
@@ -0,0 +1,68 @@
+---
+rule:
+  aip: 134
+  name: [core, '0134', http-uri-name]
+  summary: Update methods must map the resource's name field to the URI.
+---
+
+# Update methods: HTTP URI name field
+
+This rule enforces that all `Update` RPCs map the `name` field from the
+resource object to the HTTP URI, as mandated in [AIP-134][].
+
+## Details
+
+This rule looks at any message matching beginning with `Update`, and complains
+if the `name` variable from the resource (not the request message) is not
+included in the URI. It _does_ check additional bindings if they are present.
+
+Additionally, if the resource uses multiple words, it ensures that word
+separation uses `snake_case`.
+
+## Examples
+
+**Incorrect** code for this rule:
+
+```proto
+// Incorrect.
+rpc UpdateBookRequest(UpdateBookRequest) returns (Book) {
+  option (google.api.http) = {
+    post: "/v1/{name=publishers/*/books/*}"  // Should be `book.name`.
+    body: "book"
+  };
+}
+```
+
+**Correct** code for this rule:
+
+```proto
+// Correct.
+rpc UpdateBookRequest(UpdateBookRequest) returns (Book) {
+  option (google.api.http) = {
+    post: "/v1/{book.name=publishers/*/books/*}"
+    body: "book"
+  };
+}
+```
+
+## Disabling
+
+If you need to violate this rule, use a leading comment above the method.
+Remember to also include an [aip.dev/not-precedent][] comment explaining why.
+
+```proto
+// (-- api-linter: core::0134::http-uri-name=disabled
+//     aip.dev/not-precedent: We need to do this because reasons. --)
+rpc UpdateBookRequest(UpdateBookRequest) returns (Book) {
+  option (google.api.http) = {
+    post: "/v1/{name=publishers/*/books/*}"
+    body: "book"
+  };
+}
+```
+
+If you need to violate this rule for an entire file, place the comment at the
+top of the file.
+
+[aip-134]: https://aip.dev/134
+[aip.dev/not-precedent]: https://aip.dev/not-precedent
diff --git a/docs/rules/core/0134-request-mask-field.md b/docs/rules/core/0134-request-mask-field.md
new file mode 100644
index 0000000..2266472
--- /dev/null
+++ b/docs/rules/core/0134-request-mask-field.md
@@ -0,0 +1,59 @@
+---
+rule:
+  aip: 134
+  name: [core, '0134', request-mask-field]
+  summary: Update RPCs must have a field mask in the request.
+---
+
+# Update methods: Mask field
+
+This rule enforces that all `Update` standard methods have a field in the
+request message for the field mask, as mandated in [AIP-134][].
+
+## Details
+
+This rule looks at any message matching `Update*Request` and complains if it
+can not find a field named `update_mask`, or if that field has any type other
+than `google.protobuf.FieldMask`.
+
+## Examples
+
+**Incorrect** code for this rule:
+
+```proto
+// Incorrect.
+// `google.protobuf.FieldMask update_mask` is missing.
+message UpdateBookRequest {
+  Book book = 1;
+}
+```
+
+**Correct** code for this rule:
+
+```proto
+// Correct.
+message UpdateBookRequest {
+  Book book = 1;
+  google.protobuf.FieldMask update_mask = 2;
+}
+```
+
+## Disabling
+
+If you need to violate this rule, use a leading comment above the message (if
+the resource field is missing) or above the field (if it is improperly named).
+Remember to also include an [aip.dev/not-precedent][] comment explaining why.
+
+```proto
+// (-- api-linter: core::0134::request-mask-field=disabled
+//     aip.dev/not-precedent: We need to do this because reasons. --)
+message UpdateBookRequest {
+  Book book = 1;
+}
+```
+
+If you need to violate this rule for an entire file, place the comment at the
+top of the file.
+
+[aip-134]: https://aip.dev/134
+[aip.dev/not-precedent]: https://aip.dev/not-precedent
diff --git a/docs/rules/core/0134-request-message-name.md b/docs/rules/core/0134-request-message-name.md
new file mode 100644
index 0000000..18b6e2d
--- /dev/null
+++ b/docs/rules/core/0134-request-message-name.md
@@ -0,0 +1,65 @@
+---
+rule:
+  aip: 134
+  name: [core, '0134', request-message-name]
+  summary: Update methods must have standardized request message names.
+---
+
+# Update methods: Request message
+
+This rule enforces that all `Update` RPCs have a request message name of
+`Update*Request`, as mandated in [AIP-134][].
+
+## Details
+
+This rule looks at any message matching beginning with `Update`, and complains
+if the name of the corresponding input message does not match the name of the
+RPC with the suffix `Request` appended.
+
+## Examples
+
+**Incorrect** code for this rule:
+
+```proto
+// Incorrect.
+rpc UpdateBook(Book) returns (Book) {  // Should be `UpdateBookRequest`.
+  option (google.api.http) = {
+    patch: "/v1/{name=publishers/*/books/*}"
+    body: "*"
+  };
+}
+```
+
+**Correct** code for this rule:
+
+```proto
+// Correct.
+rpc UpdateBook(UpdateBookRequest) returns (Book) {
+  option (google.api.http) = {
+    patch: "/v1/{book.name=publishers/*/books/*}"
+    body: "book"
+  };
+}
+```
+
+## Disabling
+
+If you need to violate this rule, use a leading comment above the method.
+Remember to also include an [aip.dev/not-precedent][] comment explaining why.
+
+```proto
+// (-- api-linter: core::0134::request-message-name=disabled
+//     aip.dev/not-precedent: We need to do this because reasons. --)
+rpc UpdateBook(Book) returns (Book) {  // Should be `UpdateBookRequest`.
+  option (google.api.http) = {
+    patch: "/v1/{name=publishers/*/books/*}"
+    body: "*"
+  };
+}
+```
+
+If you need to violate this rule for an entire file, place the comment at the
+top of the file.
+
+[aip-134]: https://aip.dev/134
+[aip.dev/not-precedent]: https://aip.dev/not-precedent
diff --git a/docs/rules/core/0134-request-resource-field.md b/docs/rules/core/0134-request-resource-field.md
new file mode 100644
index 0000000..380a06b
--- /dev/null
+++ b/docs/rules/core/0134-request-resource-field.md
@@ -0,0 +1,67 @@
+---
+rule:
+  aip: 134
+  name: [core, '0134', request-resource-field]
+  summary: Update RPCs must have a field for the resource in the request.
+---
+
+# Update methods: Resource field
+
+This rule enforces that all `Update` standard methods have a field in the
+request message for the resource itself, as mandated in [AIP-134][].
+
+## Details
+
+This rule looks at any message matching `Update*Request` and complains if there
+is no field of the resource's type with the expected field name.
+
+## Examples
+
+**Incorrect** code for this rule:
+
+```proto
+// Incorrect.
+// `Book book` is missing.
+message UpdateBookRequest {
+  google.protobuf.FieldMask update_mask = 2;
+}
+```
+
+```proto
+// Incorrect.
+message UpdateBookRequest {
+  Book payload = 1;  // Field name should be `book`.
+  google.protobuf.FieldMask update_mask = 2;
+}
+```
+
+**Correct** code for this rule:
+
+```proto
+// Correct.
+message UpdateBookRequest {
+  Book book = 1;
+  google.protobuf.FieldMask update_mask = 2;
+}
+```
+
+## Disabling
+
+If you need to violate this rule, use a leading comment above the message (if
+the resource field is missing) or above the field (if it is improperly named).
+Remember to also include an [aip.dev/not-precedent][] comment explaining why.
+
+```proto
+// (-- api-linter: core::0134::request-resource-field=disabled
+//     aip.dev/not-precedent: We need to do this because reasons. --)
+message UpdateBookRequest {
+  Book payload = 1;
+  google.protobuf.FieldMask update_mask = 2;
+}
+```
+
+If you need to violate this rule for an entire file, place the comment at the
+top of the file.
+
+[aip-134]: https://aip.dev/134
+[aip.dev/not-precedent]: https://aip.dev/not-precedent
diff --git a/docs/rules/core/0134-request-unknown-fields.md b/docs/rules/core/0134-request-unknown-fields.md
new file mode 100644
index 0000000..f3226f9
--- /dev/null
+++ b/docs/rules/core/0134-request-unknown-fields.md
@@ -0,0 +1,65 @@
+---
+rule:
+  aip: 134
+  name: [core, '0134', request-unknown-fields]
+  summary: Update RPCs should not have unexpected fields in the request.
+---
+
+# Update methods: Unknown fields
+
+This rule enforces that all `Update` standard methods do not have unexpected
+fields, as mandated in [AIP-134][].
+
+## Details
+
+This rule looks at any message matching `Update*Request` and complains if it
+comes across any fields other than:
+
+- `{Resource} {resource}` ([AIP-134][])
+- `google.protobuf.FieldMask update_mask` ([AIP-134][])
+- `string request_id` ([AIP-155][])
+
+## Examples
+
+**Incorrect** code for this rule:
+
+```proto
+// Incorrect.
+message UpdateBookRequest {
+  Book book = 1;
+  google.protobuf.FieldMask update_mask = 2;
+  string library_id = 3;  // Non-standard field.
+}
+```
+
+**Correct** code for this rule:
+
+```proto
+// Correct.
+message UpdateBookRequest {
+  Book book = 1;
+  google.protobuf.FieldMask update_mask = 2;
+}
+```
+
+## Disabling
+
+If you need to violate this rule, use a leading comment above the field.
+Remember to also include an [aip.dev/not-precedent][] comment explaining why.
+
+```proto
+message UpdateBookRequest {
+  Book book = 1;
+  google.protobuf.FieldMask update_mask = 2;
+  // (-- api-linter: core::0134::request-unknown-fields=disabled
+  //     aip.dev/not-precedent: We really need this field because reaosns. --)
+  string library_id = 3;  // Non-standard field.
+}
+```
+
+If you need to violate this rule for an entire file, place the comment at the
+top of the file.
+
+[aip-134]: https://aip.dev/134
+[aip-155]: https://aip.dev/155
+[aip.dev/not-precedent]: https://aip.dev/not-precedent
diff --git a/docs/rules/core/0134-response-message-name.md b/docs/rules/core/0134-response-message-name.md
new file mode 100644
index 0000000..1262fdd
--- /dev/null
+++ b/docs/rules/core/0134-response-message-name.md
@@ -0,0 +1,105 @@
+---
+rule:
+  aip: 134
+  name: [core, '0134', response-message-name]
+  summary: Update methods must return the resource.
+---
+
+# Update methods: Resource response message
+
+This rule enforces that all `Update` RPCs have a response message of the
+resource, as mandated in [AIP-134][].
+
+## Details
+
+This rule looks at any message matching beginning with `Update`, and complains
+if the name of the corresponding output message does not match the name of the
+RPC with the prefix `Update` removed.
+
+It also permits a response of `google.longrunning.Operation`; in this case, it
+checks the `response_type` in the `google.longrunning.operation_info`
+annotation and ensures that _it_ corresponds to the name of the RPC with the
+prefix `Update` removed.
+
+## Examples
+
+### Standard
+
+**Incorrect** code for this rule:
+
+```proto
+// Incorrect.
+// Should be `Book`.
+rpc UpdateBook(UpdateBookRequest) returns (UpdateBookResponse) {
+  option (google.api.http) = {
+    patch: "/v1/{book.name=publishers/*/books/*}"
+    body: "book"
+  };
+}
+```
+
+**Correct** code for this rule:
+
+```proto
+// Correct.
+rpc UpdateBook(UpdateBookRequest) returns (Book) {
+  option (google.api.http) = {
+    patch: "/v1/{book.name=publishers/*/books/*}"
+    body: "book"
+  };
+}
+```
+
+### Long-running operation
+
+```proto
+// Incorrect.
+rpc UpdateBook(UpdateBookRequest) returns (google.longrunning.Operation) {
+  option (google.api.http) = {
+    patch: "/v1/{book.name=publishers/*/books/*}"
+    body: "book"
+  };
+  option (google.longrunning.operation_info) = {
+    response_type: "UpdateBookResponse"  // Should be "Book".
+    metadata_type: "UpdateBookMetadata"
+  }
+}
+```
+
+**Correct** code for this rule:
+
+```proto
+// Correct.
+rpc UpdateBook(UpdateBookRequest) returns (google.longrunning.Operation) {
+  option (google.api.http) = {
+    patch: "/v1/{book.name=publishers/*/books/*}"
+    body: "book"
+  };
+  option (google.longrunning.operation_info) = {
+    response_type: "Book"
+    metadata_type: "UpdateBookMetadata"
+  }
+}
+```
+
+## Disabling
+
+If you need to violate this rule, use a leading comment above the method.
+Remember to also include an [aip.dev/not-precedent][] comment explaining why.
+
+```proto
+// (-- api-linter: core::0134::response-message-name=disabled
+//     aip.dev/not-precedent: We need to do this because reasons. --)
+rpc UpdateBook(UpdateBookRequest) returns (UpdateBookResponse) {
+  option (google.api.http) = {
+    patch: "/v1/{book.name=publishers/*/books/*}"
+    body: "book"
+  };
+}
+```
+
+If you need to violate this rule for an entire file, place the comment at the
+top of the file.
+
+[aip-134]: https://aip.dev/134
+[aip.dev/not-precedent]: https://aip.dev/not-precedent
diff --git a/docs/rules/core/0134-synonyms.md b/docs/rules/core/0134-synonyms.md
new file mode 100644
index 0000000..0041baa
--- /dev/null
+++ b/docs/rules/core/0134-synonyms.md
@@ -0,0 +1,68 @@
+---
+rule:
+  aip: 134
+  name: [core, '0134', synonyms]
+  summary: Update methods must be named starting with "Update".
+---
+
+# Update methods: Synonym check
+
+This rule enforces that single-resource creation methods have names beginning
+with `Update`, as mandated in [AIP-134][].
+
+## Details
+
+This rule looks at any message with names similar to `Update`, and suggests
+using `Update` instead. It complains if it sees the following synonyms:
+
+- Patch
+- Put
+- Set
+
+## Examples
+
+**Incorrect** code for this rule:
+
+```proto
+// Incorrect.
+rpc PatchBook(PatchBookRequest) returns (Book) {  // Should be `UpdateBook`.
+  option (google.api.http) = {
+    patch: "/v1/{book.name=publishers/*/books/*}"
+    body: "book"
+  };
+}
+```
+
+**Correct** code for this rule:
+
+```proto
+// Correct.
+rpc UpdateBook(UpdateBookRequest) returns (Book) {
+  option (google.api.http) = {
+    patch: "/v1/{book.name=publishers/*/books/*}"
+    body: "book"
+  };
+}
+```
+
+## Disabling
+
+If you need to violate this rule, use a leading comment above the method.
+Remember to also include an [aip.dev/not-precedent][] comment explaining why.
+
+```proto
+// (-- api-linter: core::0134::synonyms=disabled
+//     aip.dev/not-precedent: We need to do this because reasons. --)
+rpc PatchBook(PatchBookRequest) returns (Book) {
+  option (google.api.http) = {
+    patch: "/v1/{book.name=publishers/*/books/*}"
+    body: "book"
+  };
+}
+```
+
+If you need to violate this rule for an entire file, place the comment at the
+top of the file.
+
+[aip-134]: https://aip.dev/134
+[aip.dev/not-precedent]: https://aip.dev/not-precedent
diff --git a/rules/aip0133/request_resource_field.go b/rules/aip0133/request_resource_field.go
index e6eb485..ab17603 100644
--- a/rules/aip0133/request_resource_field.go
+++ b/rules/aip0133/request_resource_field.go
@@ -19,11 +19,12 @@ import (
 
 	"github.com/googleapis/api-linter/lint"
 	"github.com/jhump/protoreflect/desc"
+	"github.com/stoewer/go-strcase"
 )
 
 // The create request message should have resource field.
 var resourceField = &lint.MessageRule{
-	Name:   lint.NewRuleName("core", "0133", "request-message", "resource-field"),
+	Name:   lint.NewRuleName("core", "0133", "request-resource-field"),
 	URI:    "https://aip.dev/133#request-message",
 	OnlyIf: isCreateRequestMessage,
 	LintMessage: func(m *desc.MessageDescriptor) []lint.Problem {
@@ -33,6 +34,15 @@ var resourceField = &lint.MessageRule{
 		// checked by AIP-0133 ("core::0133::http-body")
 		for _, fieldDesc := range m.GetFields() {
 			if msgDesc := fieldDesc.GetMessageType(); msgDesc != nil && msgDesc.GetName() == resourceMsgName {
+				// Rule check: Is the field named properly?
+				if want := strcase.SnakeCase(resourceMsgName); fieldDesc.GetName() != want {
+					return []lint.Problem{{
+						Message:    fmt.Sprintf("Resource field should be named %q.", want),
+						Descriptor: fieldDesc,
+						Suggestion: want,
+						Location:   lint.DescriptorNameLocation(fieldDesc),
+					}}
+				}
 				return nil
 			}
 		}
diff --git a/rules/aip0134/aip0134.go b/rules/aip0134/aip0134.go
index 49813ca..dae974e 100644
--- a/rules/aip0134/aip0134.go
+++ b/rules/aip0134/aip0134.go
@@ -17,9 +17,11 @@ package aip0134
 
 import (
 	"regexp"
+	"strings"
 
 	"github.com/googleapis/api-linter/lint"
 	"github.com/jhump/protoreflect/desc"
+	"github.com/stoewer/go-strcase"
 )
 
 // AddRules accepts a register function and registers each of
@@ -30,8 +32,9 @@ func AddRules(r lint.RuleRegistry) {
 		httpMethod,
 		httpNameField,
 		responseMessageName,
+		requestMaskField,
 		requestMessageName,
-		standardFields,
+		resourceField,
 		synonyms,
 		unknownFields,
 	)
@@ -51,3 +54,12 @@ func isUpdateMethod(m *desc.MethodDescriptor) bool {
 func isUpdateRequestMessage(m *desc.MessageDescriptor) bool {
 	return updateReqMessageRegexp.MatchString(m.GetName())
 }
+
+func extractResource(reqName string) string {
+	// Strips "Update" from the beginning and "Request" from the end.
+	return reqName[6 : len(reqName)-7]
+}
+
+func fieldNameFromResource(resource string) string {
+	return strings.ToLower(strcase.SnakeCase(resource))
+}
diff --git a/rules/aip0134/http_body.go b/rules/aip0134/http_body.go
new file mode 100644
index 0000000..87f1f00
--- /dev/null
+++ b/rules/aip0134/http_body.go
@@ -0,0 +1,45 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package aip0134
+
+import (
+	"fmt"
+
+	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/rules/internal/utils"
+	"github.com/jhump/protoreflect/desc"
+	"github.com/stoewer/go-strcase"
+)
+
+// Update methods should have an HTTP body.
+var httpBody = &lint.MethodRule{
+	Name:   lint.NewRuleName("core", "0134", "http-body"),
+	URI:    "https://aip.dev/134#guidance",
+	OnlyIf: isUpdateMethod,
+	LintMethod: func(m *desc.MethodDescriptor) []lint.Problem {
+		fieldName := strcase.SnakeCase(m.GetName()[6:])
+		// Establish that the RPC has HTTP body equal to fieldName.
+		for _, httpRule := range utils.GetHTTPRules(m) {
+			if httpRule.Body != fieldName {
+				return []lint.Problem{{
+					Message:    fmt.Sprintf("Update methods should have an HTTP body equal to `%q`.", fieldName),
+					Descriptor: m,
+				}}
+			}
+		}
+
+		return nil
+	},
+}
diff --git a/rules/aip0134/http_method.go b/rules/aip0134/http_method.go
new file mode 100644
index 0000000..e6c3bb6
--- /dev/null
+++ b/rules/aip0134/http_method.go
@@ -0,0 +1,41 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package aip0134
+
+import (
+	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/rules/internal/utils"
+	"github.com/jhump/protoreflect/desc"
+)
+
+// Update methods should use the HTTP PATCH verb.
+var httpMethod = &lint.MethodRule{
+	Name:   lint.NewRuleName("core", "0134", "http-method"),
+	URI:    "https://aip.dev/134#patch-and-put",
+	OnlyIf: isUpdateMethod,
+	LintMethod: func(m *desc.MethodDescriptor) []lint.Problem {
+		// Rule check: Establish that the RPC uses HTTP PATCH.
+		for _, httpRule := range utils.GetHTTPRules(m) {
+			if httpRule.Method != "PATCH" {
+				return []lint.Problem{{
+					Message:    "Update methods must use the HTTP PATCH verb.",
+					Descriptor: m,
+				}}
+			}
+		}
+
+		return nil
+	},
+}
diff --git a/rules/aip0134/http_uri_name.go b/rules/aip0134/http_uri_name.go
new file mode 100644
index 0000000..9036f2f
--- /dev/null
+++ b/rules/aip0134/http_uri_name.go
@@ -0,0 +1,46 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package aip0134
+
+import (
+	"fmt"
+
+	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/rules/internal/utils"
+	"github.com/jhump/protoreflect/desc"
+	"github.com/stoewer/go-strcase"
+)
+
+// Update methods should have a proper HTTP pattern.
+var httpNameField = &lint.MethodRule{
+	Name:   lint.NewRuleName("core", "0134", "http-uri-name"),
+	URI:    "https://aip.dev/134#guidance",
+	OnlyIf: isUpdateMethod,
+	LintMethod: func(m *desc.MethodDescriptor) []lint.Problem {
+		fieldName := strcase.SnakeCase(m.GetName()[6:])
+		// Establish that the RPC has expected HTTP pattern.
+		for _, httpRule := range utils.GetHTTPRules(m) {
+			matches := updateURINameRegexp.FindStringSubmatch(httpRule.URI)
+			if matches == nil || matches[1] != fieldName {
+				return []lint.Problem{{
+					Message:    fmt.Sprintf("Update methods should include the `%s.name` field in the URI.", fieldName),
+					Descriptor: m,
+				}}
+			}
+		}
+
+		return nil
+	},
+}
diff --git a/rules/aip0134/message_rules.go b/rules/aip0134/message_rules.go
deleted file mode 100644
index f83c29f..0000000
--- a/rules/aip0134/message_rules.go
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package aip0134
-
-import (
-	"fmt"
-	"strings"
-
-	"github.com/googleapis/api-linter/lint"
-	"github.com/jhump/protoreflect/desc"
-	"github.com/stoewer/go-strcase"
-)
-
-func extractResource(reqName string) string {
-	// Strips "Update" from the beginning and "Request" from the end.
-	return reqName[6 : len(reqName)-7]
-}
-
-func fieldNameFromResource(resource string) string {
-	return strings.ToLower(strcase.SnakeCase(resource))
-}
-
-// The Update standard method should only have expected fields.
-var standardFields = &lint.MessageRule{
-	Name:   lint.NewRuleName("core", "0134", "request-message", "required-fields"),
-	URI:    "https://aip.dev/134#request-message",
-	OnlyIf: isUpdateRequestMessage,
-	LintMessage: func(m *desc.MessageDescriptor) []lint.Problem {
-		r := extractResource(m.GetName())
-		fields := []struct {
-			name string
-			typ  string
-		}{
-			{fieldNameFromResource(r), r},
-			{"update_mask", "google.protobuf.FieldMask"},
-		}
-		// Rule check: Establish that expected fields are present.
-		for _, f := range fields {
-			// Rule check: Establish that the field is present.
-			field := m.FindFieldByName(f.name)
-			if field == nil {
-				return []lint.Problem{{
-					Message:    fmt.Sprintf("Method %s has no `%s` field", m.GetName(), f.name),
-					Descriptor: m,
-				}}
-			}
-
-			// Rule check: Ensure that the field is the correct type.
-			msgDesc := field.GetMessageType()
-			if msgDesc == nil || msgDesc.GetFullyQualifiedName() != f.typ {
-				return []lint.Problem{{
-					Message:    fmt.Sprintf("`%s` field on Update RPCs should be of type `%s`", f.name, f.typ),
-					Descriptor: field,
-				}}
-			}
-		}
-
-		return nil
-	},
-}
-
-// Update methods should not have unrecognized fields.
-var unknownFields = &lint.MessageRule{
-	Name:   lint.NewRuleName("core", "0134", "request-message", "unknown-fields"),
-	URI:    "https://aip.dev/134#request-message",
-	OnlyIf: isUpdateRequestMessage,
-	LintMessage: func(m *desc.MessageDescriptor) (problems []lint.Problem) {
-		resource := extractResource(m.GetName())
-		// Rule check: Establish that there are no unexpected fields.
-		allowedFields := map[string]struct{}{
-			fieldNameFromResource(resource): {}, // AIP-134
-			"update_mask":                   {}, // AIP-134
-		}
-		for _, field := range m.GetFields() {
-			if _, ok := allowedFields[string(field.GetName())]; !ok {
-				problems = append(problems, lint.Problem{
-					Message: fmt.Sprintf(
-						"Unexpected field: Update RPCs must only contain fields explicitly described in AIPs, not %q.",
-						string(field.GetName()),
-					),
-					Descriptor: field,
-				})
-			}
-		}
-
-		return
-	},
-}
diff --git a/rules/aip0134/method_rules.go b/rules/aip0134/method_rules.go
deleted file mode 100644
index f0ae0c8..0000000
--- a/rules/aip0134/method_rules.go
+++ /dev/null
@@ -1,170 +0,0 @@
-// Copyright 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package aip0134
-
-import (
-	"fmt"
-	"strings"
-
-	"github.com/googleapis/api-linter/lint"
-	"github.com/googleapis/api-linter/rules/internal/utils"
-	"github.com/jhump/protoreflect/desc"
-	"github.com/stoewer/go-strcase"
-)
-
-// Update methods should have a properly named Request message.
-var requestMessageName = &lint.MethodRule{
-	Name:   lint.NewRuleName("core", "0134", "request-message", "name"),
-	URI:    "https://aip.dev/134#guidance",
-	OnlyIf: isUpdateMethod,
-	LintMethod: func(m *desc.MethodDescriptor) []lint.Problem {
-		// Rule check: Establish that for methods such as `UpdateFoo`, the request
-		// message is named `UpdateFooRequest`.
-		if got, want := m.GetInputType().GetName(), m.GetName()+"Request"; got != want {
-			return []lint.Problem{{
-				Message: fmt.Sprintf(
-					"Update RPCs should have a request message named after the RPC, such as %q.",
-					want,
-				),
-				Suggestion: want,
-				Descriptor: m,
-			}}
-		}
-
-		return nil
-	},
-}
-
-// Update methods should use the resource as the response message
-var responseMessageName = &lint.MethodRule{
-	Name:   lint.NewRuleName("core", "0134", "response-message", "name"),
-	URI:    "https://aip.dev/134#guidance",
-	OnlyIf: isUpdateMethod,
-	LintMethod: func(m *desc.MethodDescriptor) []lint.Problem {
-		// Rule check: Establish that for methods such as `UpdateFoo`, the response
-		// message is `Foo` or `google.longrunning.Operation`.
-		want := strings.Replace(m.GetName(), "Update", "", 1)
-		got := m.GetOutputType().GetName()
-
-		// If the return type is an LRO, use the annotated response type instead.
-		if m.GetOutputType().GetFullyQualifiedName() == "google.longrunning.Operation" {
-			got = utils.GetOperationInfo(m).GetResponseType()
-		}
-
-		// Return a problem if we did not get the expected return name.
-		//
-		// Note: If `got` is empty string, this is an unannotated LRO.
-		// The AIP-151 rule will whine about that, and this rule should not as it
-		// would be confusing.
-		if got != want && got != "" {
-			return []lint.Problem{{
-				Message: fmt.Sprintf(
-					"Update RPCs should have response message type %q, not %q.",
-					want,
-					got,
-				),
-				Suggestion: want,
-				Descriptor: m,
-			}}
-		}
-		return nil
-	},
-}
-
-// Update methods should use the HTTP PATCH verb.
-var httpMethod = &lint.MethodRule{
-	Name:   lint.NewRuleName("core", "0134", "http-method"),
-	URI:    "https://aip.dev/134#patch-and-put",
-	OnlyIf: isUpdateMethod,
-	LintMethod: func(m *desc.MethodDescriptor) []lint.Problem {
-		// Rule check: Establish that the RPC uses HTTP PATCH.
-		for _, httpRule := range utils.GetHTTPRules(m) {
-			if httpRule.Method != "PATCH" {
-				return []lint.Problem{{
-					Message:    "Update methods must use the HTTP PATCH verb.",
-					Descriptor: m,
-				}}
-			}
-		}
-
-		return nil
-	},
-}
-
-// Update methods should have a proper HTTP pattern.
-var httpNameField = &lint.MethodRule{
-	Name:   lint.NewRuleName("core", "0134", "http-name"),
-	URI:    "https://aip.dev/134#guidance",
-	OnlyIf: isUpdateMethod,
-	LintMethod: func(m *desc.MethodDescriptor) []lint.Problem {
-		fieldName := strcase.SnakeCase(m.GetName()[6:])
-		// Establish that the RPC has expected HTTP pattern.
-		for _, httpRule := range utils.GetHTTPRules(m) {
-			matches := updateURINameRegexp.FindStringSubmatch(httpRule.URI)
-			if matches == nil || matches[1] != fieldName {
-				return []lint.Problem{{
-					Message:    fmt.Sprintf("Update methods should include the `%s.name` field in the URI.", fieldName),
-					Descriptor: m,
-				}}
-			}
-		}
-
-		return nil
-	},
-}
-
-// Update methods should have an HTTP body.
-var httpBody = &lint.MethodRule{
-	Name:   lint.NewRuleName("core", "0134", "http-body"),
-	URI:    "https://aip.dev/134#guidance",
-	OnlyIf: isUpdateMethod,
-	LintMethod: func(m *desc.MethodDescriptor) []lint.Problem {
-		fieldName := strcase.SnakeCase(m.GetName()[6:])
-		// Establish that the RPC has HTTP body equal to fieldName.
-		for _, httpRule := range utils.GetHTTPRules(m) {
-			if httpRule.Body != fieldName {
-				return []lint.Problem{{
-					Message:    fmt.Sprintf("Update methods should have an HTTP body equal to `%q`.", fieldName),
-					Descriptor: m,
-				}}
-			}
-		}
-
-		return nil
-	},
-}
-
-// Update methods should use the word "update", not synonyms.
-var synonyms = &lint.MethodRule{
-	Name: lint.NewRuleName("core", "0134", "synonyms"),
-	URI:  "https://aip.dev/134#guidance",
-	LintMethod: func(m *desc.MethodDescriptor) []lint.Problem {
-		name := m.GetName()
-		for _, syn := range []string{"Patch", "Put", "Set"} {
-			if strings.HasPrefix(name, syn) {
-				return []lint.Problem{{
-					Message: fmt.Sprintf(
-						`%q can be a synonym for "Update". Should this be a Update method?`,
-						syn,
-					),
-					Descriptor: m,
-					Location:   lint.DescriptorNameLocation(m),
-					Suggestion: strings.Replace(name, syn, "Update", 1),
-				}}
-			}
-		}
-		return nil
-	},
-}
diff --git a/rules/aip0134/request_mask_field.go b/rules/aip0134/request_mask_field.go
new file mode 100644
index 0000000..c33ef50
--- /dev/null
+++ b/rules/aip0134/request_mask_field.go
@@ -0,0 +1,42 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package aip0134
+
+import (
+	"github.com/googleapis/api-linter/lint"
+	"github.com/jhump/protoreflect/desc"
+)
+
+var requestMaskField = &lint.MessageRule{
+	Name:   lint.NewRuleName("core", "0134", "request-mask-field"),
+	URI:    "https://aip.dev/134",
+	OnlyIf: isUpdateRequestMessage,
+	LintMessage: func(m *desc.MessageDescriptor) []lint.Problem {
+		updateMask := m.FindFieldByName("update_mask")
+		if updateMask == nil {
+			return []lint.Problem{{
+				Message:    "Update methods should have an `update_mask` field.",
+				Descriptor: m,
+			}}
+		}
+		if t := updateMask.GetMessageType(); t == nil || t.GetFullyQualifiedName() != "google.protobuf.FieldMask" {
+			return []lint.Problem{{
+				Message:    "The `update_mask` field should be a google.protobuf.FieldMask.",
+				Descriptor: updateMask,
+			}}
+		}
+		return nil
+	},
+}
diff --git a/rules/aip0134/request_message_name.go b/rules/aip0134/request_message_name.go
new file mode 100644
index 0000000..c283aa0
--- /dev/null
+++ b/rules/aip0134/request_message_name.go
@@ -0,0 +1,45 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package aip0134
+
+import (
+	"fmt"
+
+	"github.com/googleapis/api-linter/lint"
+	"github.com/jhump/protoreflect/desc"
+)
+
+// Update methods should have a properly named Request message.
+var requestMessageName = &lint.MethodRule{
+	Name:   lint.NewRuleName("core", "0134", "request-message-name"),
+	URI:    "https://aip.dev/134#guidance",
+	OnlyIf: isUpdateMethod,
+	LintMethod: func(m *desc.MethodDescriptor) []lint.Problem {
+		// Rule check: Establish that for methods such as `UpdateFoo`, the request
+		// message is named `UpdateFooRequest`.
+		if got, want := m.GetInputType().GetName(), m.GetName()+"Request"; got != want {
+			return []lint.Problem{{
+				Message: fmt.Sprintf(
+					"Update RPCs should have a request message named after the RPC, such as %q.",
+					want,
+				),
+				Suggestion: want,
+				Descriptor: m,
+			}}
+		}
+
+		return nil
+	},
+}
diff --git a/rules/aip0134/request_resource_field.go b/rules/aip0134/request_resource_field.go
new file mode 100644
index 0000000..5a485fc
--- /dev/null
+++ b/rules/aip0134/request_resource_field.go
@@ -0,0 +1,56 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package aip0134
+
+import (
+	"fmt"
+
+	"github.com/googleapis/api-linter/lint"
+	"github.com/jhump/protoreflect/desc"
+	"github.com/stoewer/go-strcase"
+)
+
+// The create request message should have resource field.
+var resourceField = &lint.MessageRule{
+	Name:   lint.NewRuleName("core", "0134", "request--resource-field"),
+	URI:    "https://aip.dev/134#request-message",
+	OnlyIf: isUpdateRequestMessage,
+	LintMessage: func(m *desc.MessageDescriptor) []lint.Problem {
+		resourceMsgName := extractResource(m.GetName())
+
+		// The rule (resource field name must map to the POST body) is
+		// checked by AIP-0134 ("core::0134::http-body")
+		for _, fieldDesc := range m.GetFields() {
+			if msgDesc := fieldDesc.GetMessageType(); msgDesc != nil && msgDesc.GetName() == resourceMsgName {
+				// Rule check: Is the field named properly?
+				if want := strcase.SnakeCase(resourceMsgName); fieldDesc.GetName() != want {
+					return []lint.Problem{{
+						Message:    fmt.Sprintf("Resource field should be named %q.", want),
+						Descriptor: fieldDesc,
+						Suggestion: want,
+						Location:   lint.DescriptorNameLocation(fieldDesc),
+					}}
+				}
+				return nil
+			}
+		}
+
+		// Rule check: Establish that a resource field must be included.
+		return []lint.Problem{{
+			Message:    fmt.Sprintf("Message %q has no %q type field", m.GetName(), resourceMsgName),
+			Descriptor: m,
+		}}
+	},
+}
diff --git a/rules/aip0134/request_unknown_fields.go b/rules/aip0134/request_unknown_fields.go
new file mode 100644
index 0000000..c1193b4
--- /dev/null
+++ b/rules/aip0134/request_unknown_fields.go
@@ -0,0 +1,52 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package aip0134
+
+import (
+	"fmt"
+
+	"bitbucket.org/creachadair/stringset"
+	"github.com/googleapis/api-linter/lint"
+	"github.com/jhump/protoreflect/desc"
+)
+
+// Update methods should not have unrecognized fields.
+var unknownFields = &lint.MessageRule{
+	Name:   lint.NewRuleName("core", "0134", "request-unknown-fields"),
+	URI:    "https://aip.dev/134#request-message",
+	OnlyIf: isUpdateRequestMessage,
+	LintMessage: func(m *desc.MessageDescriptor) (problems []lint.Problem) {
+		resource := extractResource(m.GetName())
+		// Rule check: Establish that there are no unexpected fields.
+		allowedFields := stringset.New(
+			fieldNameFromResource(resource), // AIP-134
+			"update_mask",                   // AIP-134
+			"request_id",                    // AIP-155
+		)
+		for _, field := range m.GetFields() {
+			if !allowedFields.Contains(field.GetName()) {
+				problems = append(problems, lint.Problem{
+					Message: fmt.Sprintf(
+						"Unexpected field: Update RPCs must only contain fields explicitly described in AIPs, not %q.",
+						field.GetName(),
+					),
+					Descriptor: field,
+				})
+			}
+		}
+
+		return
+	},
+}
diff --git a/rules/aip0134/response_message_name.go b/rules/aip0134/response_message_name.go
new file mode 100644
index 0000000..d2f0110
--- /dev/null
+++ b/rules/aip0134/response_message_name.go
@@ -0,0 +1,60 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package aip0134
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/rules/internal/utils"
+	"github.com/jhump/protoreflect/desc"
+)
+
+// Update methods should use the resource as the response message
+var responseMessageName = &lint.MethodRule{
+	Name:   lint.NewRuleName("core", "0134", "response-message-name"),
+	URI:    "https://aip.dev/134#guidance",
+	OnlyIf: isUpdateMethod,
+	LintMethod: func(m *desc.MethodDescriptor) []lint.Problem {
+		// Rule check: Establish that for methods such as `UpdateFoo`, the response
+		// message is `Foo` or `google.longrunning.Operation`.
+		want := strings.Replace(m.GetName(), "Update", "", 1)
+		got := m.GetOutputType().GetName()
+
+		// If the return type is an LRO, use the annotated response type instead.
+		if m.GetOutputType().GetFullyQualifiedName() == "google.longrunning.Operation" {
+			got = utils.GetOperationInfo(m).GetResponseType()
+		}
+
+		// Return a problem if we did not get the expected return name.
+		//
+		// Note: If `got` is empty string, this is an unannotated LRO.
+		// The AIP-151 rule will whine about that, and this rule should not as it
+		// would be confusing.
+		if got != want && got != "" {
+			return []lint.Problem{{
+				Message: fmt.Sprintf(
+					"Update RPCs should have response message type %q, not %q.",
+					want,
+					got,
+				),
+				Suggestion: want,
+				Descriptor: m,
+			}}
+		}
+		return nil
+	},
+}
diff --git a/rules/aip0134/synonyms.go b/rules/aip0134/synonyms.go
new file mode 100644
index 0000000..fd209d4
--- /dev/null
+++ b/rules/aip0134/synonyms.go
@@ -0,0 +1,46 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package aip0134
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/googleapis/api-linter/lint"
+	"github.com/jhump/protoreflect/desc"
+)
+
+// Update methods should use the word "update", not synonyms.
+var synonyms = &lint.MethodRule{
+	Name: lint.NewRuleName("core", "0134", "synonyms"),
+	URI:  "https://aip.dev/134#guidance",
+	LintMethod: func(m *desc.MethodDescriptor) []lint.Problem {
+		name := m.GetName()
+		for _, syn := range []string{"Patch", "Put", "Set"} {
+			if strings.HasPrefix(name, syn) {
+				return []lint.Problem{{
+					Message: fmt.Sprintf(
+						`%q can be a synonym for "Update". Should this be a Update method?`,
+						syn,
+					),
+					Descriptor: m,
+					Location:   lint.DescriptorNameLocation(m),
+					Suggestion: strings.Replace(name, syn, "Update", 1),
+				}}
+			}
+		}
+		return nil
+	},
+}


Test patch:
diff --git a/rules/aip0133/request_resource_field_test.go b/rules/aip0133/request_resource_field_test.go
index 1b86469..9b47681 100644
--- a/rules/aip0133/request_resource_field_test.go
+++ b/rules/aip0133/request_resource_field_test.go
@@ -18,6 +18,7 @@ import (
 	"testing"
 
 	"github.com/googleapis/api-linter/rules/internal/testutils"
+	"github.com/jhump/protoreflect/desc"
 	"github.com/jhump/protoreflect/desc/builder"
 )
 
@@ -27,20 +28,32 @@ func TestResourceField(t *testing.T) {
 		testName     string
 		messageName  string
 		messageField *field
+		descriptor   func(*desc.MessageDescriptor) desc.Descriptor
 		problems     testutils.Problems
 	}{
 		{
 			"Valid",
 			"CreateBookRequest",
 			&field{"book", builder.FieldTypeMessage(builder.NewMessage("Book"))},
+			nil,
 			testutils.Problems{},
 		},
 		{
 			"MissingField",
 			"CreateBookRequest",
 			nil,
+			nil,
 			testutils.Problems{{Message: "Message \"CreateBookRequest\" has no \"Book\" type field"}},
 		},
+		{
+			"WrongName",
+			"CreateBookRequest",
+			&field{"payload", builder.FieldTypeMessage(builder.NewMessage("Book"))},
+			func(m *desc.MessageDescriptor) desc.Descriptor {
+				return m.GetFields()[0]
+			},
+			testutils.Problems{{Suggestion: "book"}},
+		},
 	}
 
 	// Run each test individually.
@@ -60,9 +73,14 @@ func TestResourceField(t *testing.T) {
 				t.Fatalf("Could not build %s message.", test.messageName)
 			}
 
+			var d desc.Descriptor = message
+			if test.descriptor != nil {
+				d = test.descriptor(message)
+			}
+
 			// Run the lint rule, and establish that it returns the correct problems.
 			problems := resourceField.Lint(message.GetFile())
-			if diff := test.problems.SetDescriptor(message).Diff(problems); diff != "" {
+			if diff := test.problems.SetDescriptor(d).Diff(problems); diff != "" {
 				t.Errorf(diff)
 			}
 		})
diff --git a/rules/aip0134/http_body_test.go b/rules/aip0134/http_body_test.go
new file mode 100644
index 0000000..7f391da
--- /dev/null
+++ b/rules/aip0134/http_body_test.go
@@ -0,0 +1,74 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package aip0134
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/golang/protobuf/proto"
+	dpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
+	"github.com/jhump/protoreflect/desc/builder"
+	"google.golang.org/genproto/googleapis/api/annotations"
+)
+
+func TestHttpBody(t *testing.T) {
+	tests := []struct {
+		testName   string
+		body       string
+		methodName string
+		msg        string
+	}{
+		{"Valid", "book", "UpdateBook", ""},
+		{"InvalidFoo", "foo", "UpdateBook", "HTTP body"},
+		{"InvalidStar", "*", "UpdateBook", "HTTP body"},
+		{"InvalidEmpty", "", "UpdateBook", "HTTP body"},
+		{"Irrelevant", "*", "AcquireBook", ""},
+	}
+
+	for _, test := range tests {
+		t.Run(test.testName, func(t *testing.T) {
+			// Create a MethodOptions with the annotation set.
+			opts := &dpb.MethodOptions{}
+			httpRule := &annotations.HttpRule{
+				Pattern: &annotations.HttpRule_Patch{
+					Patch: "/v1/{book.name=publishers/*/books/*}",
+				},
+				Body: test.body,
+			}
+			if err := proto.SetExtension(opts, annotations.E_Http, httpRule); err != nil {
+				t.Fatalf("Failed to set google.api.http annotation.")
+			}
+
+			// Create a minimal service with a AIP-134 Update method
+			// (or with a different method, in the "Irrelevant" case).
+			service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName,
+				builder.RpcTypeMessage(builder.NewMessage("UpdateBookRequest"), false),
+				builder.RpcTypeMessage(builder.NewMessage("Book"), false),
+			).SetOptions(opts)).Build()
+			if err != nil {
+				t.Fatalf("Could not build %s method.", test.methodName)
+			}
+
+			// Run the method, ensure we get what we expect.
+			problems := httpBody.Lint(service.GetFile())
+			if test.msg == "" && len(problems) > 0 {
+				t.Errorf("Got %v, expected no problems.", problems)
+			} else if test.msg != "" && !strings.Contains(problems[0].Message, test.msg) {
+				t.Errorf("Got %q, expected message containing %q", problems[0].Message, test.msg)
+			}
+		})
+	}
+}
diff --git a/rules/aip0134/http_method_test.go b/rules/aip0134/http_method_test.go
new file mode 100644
index 0000000..deace20
--- /dev/null
+++ b/rules/aip0134/http_method_test.go
@@ -0,0 +1,80 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package aip0134
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/golang/protobuf/proto"
+	dpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
+	"github.com/jhump/protoreflect/desc/builder"
+	"google.golang.org/genproto/googleapis/api/annotations"
+)
+
+func TestHttpMethod(t *testing.T) {
+	// Set up POST and PATCH HTTP annotations.
+	httpPost := &annotations.HttpRule{
+		Pattern: &annotations.HttpRule_Post{
+			Post: "/v1/{book.name=publishers/*/books/*}",
+		},
+	}
+	httpPatch := &annotations.HttpRule{
+		Pattern: &annotations.HttpRule_Patch{
+			Patch: "/v1/{book.name=publishers/*/books/*}",
+		},
+	}
+
+	// Set up testing permutations.
+	tests := []struct {
+		testName   string
+		httpRule   *annotations.HttpRule
+		methodName string
+		msg        string
+	}{
+		{"Valid", httpPatch, "UpdateBook", ""},
+		{"Invalid", httpPost, "UpdateBook", "HTTP PATCH"},
+		{"Irrelevant", httpPost, "AcquireBook", ""},
+	}
+
+	// Run each test.
+	for _, test := range tests {
+		t.Run(test.testName, func(t *testing.T) {
+			// Create a MethodOptions with the annotation set.
+			opts := &dpb.MethodOptions{}
+			if err := proto.SetExtension(opts, annotations.E_Http, test.httpRule); err != nil {
+				t.Fatalf("Failed to set google.api.http annotation.")
+			}
+
+			// Create a minimal service with a AIP-134 Update method
+			// (or with a different method, in the "Irrelevant" case).
+			service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName,
+				builder.RpcTypeMessage(builder.NewMessage("UpdateBookRequest"), false),
+				builder.RpcTypeMessage(builder.NewMessage("Book"), false),
+			).SetOptions(opts)).Build()
+			if err != nil {
+				t.Fatalf("Could not build %s method.", test.methodName)
+			}
+
+			// Run the method, ensure we get what we expect.
+			problems := httpMethod.Lint(service.GetFile())
+			if test.msg == "" && len(problems) > 0 {
+				t.Errorf("Got %v, expected no problems.", problems)
+			} else if test.msg != "" && !strings.Contains(problems[0].Message, test.msg) {
+				t.Errorf("Got %q, expected message containing %q", problems[0].Message, test.msg)
+			}
+		})
+	}
+}
diff --git a/rules/aip0134/http_uri_name_test.go b/rules/aip0134/http_uri_name_test.go
new file mode 100644
index 0000000..79db440
--- /dev/null
+++ b/rules/aip0134/http_uri_name_test.go
@@ -0,0 +1,84 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package aip0134
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/golang/protobuf/proto"
+	dpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
+	"github.com/jhump/protoreflect/desc/builder"
+	"google.golang.org/genproto/googleapis/api/annotations"
+)
+
+func TestHttpNameField(t *testing.T) {
+	tests := []struct {
+		testName   string
+		uri        string
+		methodName string
+		msg        string
+	}{
+		{"Valid", "/v1/{big_book.name=publishers/*/books/*}",
+			"UpdateBigBook", ""},
+		{"InvalidNoUnderscore", "/v1/{bigbook.name=publishers/*/books/*}",
+			"UpdateBigBook", "`big_book.name` field"},
+		{"InvalidVarNameBook", "/v1/{big_book=publishers/*/books/*}",
+			"UpdateBigBook", "`big_book.name` field"},
+		{"InvalidVarNameName", "/v1/{name=publishers/*/books/*}",
+			"UpdateBigBook", "`big_book.name` field"},
+		{"InvalidVarNameReversed", "/v1/{name.big_book=publishers/*/books/*}",
+			"UpdateBigBook", "`big_book.name` field"},
+		{"NoVarName", "/v1/publishers/*/books/*",
+			"UpdateBigBook", "`big_book.name` field"},
+		{"Irrelevant", "/v1/{book=publishers/*/books/*}",
+			"AcquireBigBook", ""},
+	}
+
+	for _, test := range tests {
+		t.Run(test.testName, func(t *testing.T) {
+			// Create a MethodOptions with the annotation set.
+			opts := &dpb.MethodOptions{}
+			httpRule := &annotations.HttpRule{
+				Pattern: &annotations.HttpRule_Patch{
+					Patch: test.uri,
+				},
+			}
+			if err := proto.SetExtension(opts, annotations.E_Http, httpRule); err != nil {
+				t.Fatalf("Failed to set google.api.http annotation.")
+			}
+
+			// Create a minimal service with a AIP-134 Update method
+			// (or with a different method, in the "Irrelevant" case).
+			service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName,
+				builder.RpcTypeMessage(builder.NewMessage("UpdateBigBookRequest"), false),
+				builder.RpcTypeMessage(builder.NewMessage("BigBook"), false),
+			).SetOptions(opts)).Build()
+			if err != nil {
+				t.Fatalf("Could not build %s method.", test.methodName)
+			}
+
+			// Run the method, ensure we get what we expect.
+			problems := httpNameField.Lint(service.GetFile())
+			if test.msg == "" && len(problems) > 0 {
+				t.Errorf("Got %v, expected no problems.", problems)
+			} else if test.msg != "" && len(problems) == 0 {
+				t.Errorf("Got no problems, expected 1.")
+			} else if test.msg != "" && !strings.Contains(problems[0].Message, test.msg) {
+				t.Errorf("Got %q, expected message containing %q", problems[0].Message, test.msg)
+			}
+		})
+	}
+}
diff --git a/rules/aip0134/message_rules_test.go b/rules/aip0134/message_rules_test.go
deleted file mode 100644
index 63e504c..0000000
--- a/rules/aip0134/message_rules_test.go
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package aip0134
-
-import (
-	"testing"
-
-	"github.com/googleapis/api-linter/rules/internal/testutils"
-	"github.com/jhump/protoreflect/desc"
-	"github.com/jhump/protoreflect/desc/builder"
-	fpb "google.golang.org/genproto/protobuf/field_mask"
-)
-
-func TestStandardFields(t *testing.T) {
-	// Get the correct message type for google.protobuf.FieldMask.
-	fieldMask, err := desc.LoadMessageDescriptorForMessage(&fpb.FieldMask{})
-	if err != nil {
-		t.Fatalf("Unable to load the field mask message.")
-	}
-
-	// Set up the testing permutations.
-	tests := []struct {
-		testName    string
-		messageName string
-		fieldName   string
-		problems    testutils.Problems
-	}{
-		// We use BigBook instead of Book in order to test correct casing logic
-		{"Valid", "UpdateBigBookRequest", "big_book", testutils.Problems{}},
-		{"NoResource", "UpdateBigBookRequest", "id", testutils.Problems{{Message: "book"}}},
-		{"Irrelevant", "AcquireBigBookRequest", "id", testutils.Problems{}},
-	}
-
-	// Run each test individually.
-	for _, test := range tests {
-		t.Run(test.testName, func(t *testing.T) {
-			// Create an appropriate message descriptor.
-			bookMsg := builder.NewMessage("BigBook")
-			message, err := builder.NewMessage(test.messageName).AddField(
-				builder.NewField(test.fieldName, builder.FieldTypeMessage(bookMsg)),
-			).AddField(
-				builder.NewField("update_mask", builder.FieldTypeImportedMessage(fieldMask)),
-			).Build()
-			if err != nil {
-				t.Fatalf("Could not build %s message.", test.messageName)
-			}
-
-			// Run the lint rule, and establish that it returns the correct problems.
-			problems := standardFields.Lint(message.GetFile())
-			if diff := test.problems.SetDescriptor(message).Diff(problems); diff != "" {
-				t.Errorf("Problems did not match: %v", diff)
-			}
-		})
-	}
-}
-
-func TestStandardFieldsMissingUpdateMask(t *testing.T) {
-	// Create an appropriate message descriptor.
-	bookMsg := builder.NewMessage("Book")
-	message, err := builder.NewMessage("UpdateBookRequest").AddField(
-		builder.NewField("book", builder.FieldTypeMessage(bookMsg)),
-	).Build()
-	if err != nil {
-		t.Fatalf("Could not build descriptor.")
-	}
-
-	// Run the lint rule, and establish that it returns the correct
-	// number of problems.
-	wantProblems := testutils.Problems{{
-		Descriptor: message,
-		Message:    "Method UpdateBookRequest has no `update_mask` field",
-	}}
-	gotProblems := standardFields.Lint(message.GetFile())
-	if diff := wantProblems.Diff(gotProblems); diff != "" {
-		t.Errorf(diff)
-	}
-}
-
-func TestStandardFieldsInvalidType(t *testing.T) {
-	// Create an appropriate message descriptor.
-	parchmentMsg := builder.NewMessage("Parchment")
-	message, err := builder.NewMessage("UpdateBookRequest").AddField(
-		builder.NewField("book", builder.FieldTypeMessage(parchmentMsg)),
-	).Build()
-	if err != nil {
-		t.Fatalf("Could not build descriptor.")
-	}
-
-	// Run the lint rule, and establish that it returns the correct
-	// number of problems.
-	wantProblems := testutils.Problems{{
-		Descriptor: message.GetFields()[0],
-		Message:    "`book` field on Update RPCs should be of type `Book`",
-	}}
-	gotProblems := standardFields.Lint(message.GetFile())
-	if diff := wantProblems.Diff(gotProblems); diff != "" {
-		t.Errorf(diff)
-	}
-}
-
-func TestUnknownFields(t *testing.T) {
-	// Get the correct message type for google.protobuf.FieldMask.
-	fieldMask, err := desc.LoadMessageDescriptorForMessage(&fpb.FieldMask{})
-	if err != nil {
-		t.Fatalf("Unable to load the field mask message.")
-	}
-
-	// Set up the testing permutations.
-	tests := []struct {
-		testName    string
-		messageName string
-		fieldName   string
-		fieldType   *builder.FieldType
-		problems    testutils.Problems
-	}{
-		// Use BigBook instead of Book to test correct casing logic
-		{"UpdateMask", "UpdateBigBookRequest", "update_mask",
-			builder.FieldTypeImportedMessage(fieldMask), testutils.Problems{}},
-		{"Invalid", "UpdateBigBookRequest", "application_id",
-			builder.FieldTypeString(), testutils.Problems{{Message: "Unexpected field"}}},
-		{"InvalidCasing", "UpdateBigBookRequest", "bigbook",
-			builder.FieldTypeString(), testutils.Problems{{Message: "Unexpected field"}}},
-		{"Irrelevant", "AcquireBigBookRequest", "application_id",
-			builder.FieldTypeString(), testutils.Problems{}},
-	}
-
-	// Run each test individually.
-	for _, test := range tests {
-		t.Run(test.testName, func(t *testing.T) {
-			// Create an appropriate message descriptor.
-			message, err := builder.NewMessage(test.messageName).AddField(
-				builder.NewField("big_book", builder.FieldTypeMessage(builder.NewMessage("BigBook"))),
-			).AddField(
-				builder.NewField(test.fieldName, test.fieldType),
-			).Build()
-			if err != nil {
-				t.Fatalf("Could not build UpdateBookRequest message.")
-			}
-
-			// Run the lint rule, and establish that it returns the correct problems.
-			wantProblems := test.problems.SetDescriptor(message.FindFieldByName(test.fieldName))
-			gotProblems := unknownFields.Lint(message.GetFile())
-			if diff := wantProblems.Diff(gotProblems); diff != "" {
-				t.Errorf(diff)
-			}
-		})
-	}
-}
diff --git a/rules/aip0134/method_rules_test.go b/rules/aip0134/method_rules_test.go
deleted file mode 100644
index 2de0a9c..0000000
--- a/rules/aip0134/method_rules_test.go
+++ /dev/null
@@ -1,299 +0,0 @@
-// Copyright 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package aip0134
-
-import (
-	"strings"
-	"testing"
-
-	"github.com/golang/protobuf/proto"
-	dpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
-	"github.com/googleapis/api-linter/rules/internal/testutils"
-	"github.com/jhump/protoreflect/desc/builder"
-	"google.golang.org/genproto/googleapis/api/annotations"
-)
-
-func TestRequestMessageName(t *testing.T) {
-	// Set up the testing permutations.
-	tests := []struct {
-		testName       string
-		methodName     string
-		reqMessageName string
-		problems       testutils.Problems
-	}{
-		{"Valid", "UpdateBook", "UpdateBookRequest", testutils.Problems{}},
-		{"Invalid", "UpdateBook", "Book", testutils.Problems{{Suggestion: "UpdateBookRequest"}}},
-		{"Irrelevant", "AcquireBook", "Book", testutils.Problems{}},
-	}
-
-	// Run each test individually.
-	for _, test := range tests {
-		t.Run(test.testName, func(t *testing.T) {
-			// Create a minimal service with a AIP-134 Update method
-			// (or with a different method, in the "Irrelevant" case).
-			service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName,
-				builder.RpcTypeMessage(builder.NewMessage(test.reqMessageName), false),
-				builder.RpcTypeMessage(builder.NewMessage("Book"), false),
-			)).Build()
-			if err != nil {
-				t.Fatalf("Could not build %s method.", test.methodName)
-			}
-
-			// Run the lint rule, and establish that it returns the expected problems.
-			problems := requestMessageName.Lint(service.GetFile())
-			if diff := test.problems.SetDescriptor(service.GetMethods()[0]).Diff(problems); diff != "" {
-				t.Errorf(diff)
-			}
-		})
-	}
-}
-
-func TestResponseMessageName(t *testing.T) {
-	// Set up the testing permutations.
-	tests := []struct {
-		testName     string
-		MethodName   string
-		RespTypeName string
-		LRO          bool
-		problems     testutils.Problems
-	}{
-		{"ValidResource", "UpdateBook", "Book", false, testutils.Problems{}},
-		{"ValidLRO", "UpdateBook", "Book", true, testutils.Problems{}},
-		{"Invalid", "UpdateBook", "UpdateBookResponse", false, testutils.Problems{{Suggestion: "Book"}}},
-		{"InvalidLRO", "UpdateBook", "UpdateBookResponse", true, testutils.Problems{{Suggestion: "Book"}}},
-		{"Irrelevant", "MutateBook", "MutateBookResponse", false, testutils.Problems{}},
-	}
-
-	// Run each test individually.
-	for _, test := range tests {
-		t.Run(test.testName, func(t *testing.T) {
-			// Create a minimal service with a AIP-134 Update method
-			file := testutils.ParseProto3Tmpl(t, `
-				import "google/longrunning/operations.proto";
-				service Library {
-					rpc {{.MethodName}}({{.MethodName}}Request)
-							returns ({{ if .LRO }}google.longrunning.Operation{{ else }}{{ .RespTypeName }}{{ end }}) {
-						{{ if .LRO -}}
-						option (google.longrunning.operation_info) = {
-							response_type: "{{.RespTypeName}}"
-							metadata_type: "{{.MethodName}}Metadata"
-						};
-						{{ end -}}
-					}
-				}
-				message {{.MethodName}}Request {}
-				message {{.RespTypeName}} {}
-			`, test)
-
-			// Run the lint rule, and establish that it returns the correct
-			// number of problems.
-			problems := responseMessageName.Lint(file)
-			method := file.GetServices()[0].GetMethods()[0]
-			if diff := test.problems.SetDescriptor(method).Diff(problems); diff != "" {
-				t.Errorf(diff)
-			}
-		})
-	}
-}
-
-func TestHttpMethod(t *testing.T) {
-	// Set up POST and PATCH HTTP annotations.
-	httpPost := &annotations.HttpRule{
-		Pattern: &annotations.HttpRule_Post{
-			Post: "/v1/{book.name=publishers/*/books/*}",
-		},
-	}
-	httpPatch := &annotations.HttpRule{
-		Pattern: &annotations.HttpRule_Patch{
-			Patch: "/v1/{book.name=publishers/*/books/*}",
-		},
-	}
-
-	// Set up testing permutations.
-	tests := []struct {
-		testName   string
-		httpRule   *annotations.HttpRule
-		methodName string
-		msg        string
-	}{
-		{"Valid", httpPatch, "UpdateBook", ""},
-		{"Invalid", httpPost, "UpdateBook", "HTTP PATCH"},
-		{"Irrelevant", httpPost, "AcquireBook", ""},
-	}
-
-	// Run each test.
-	for _, test := range tests {
-		t.Run(test.testName, func(t *testing.T) {
-			// Create a MethodOptions with the annotation set.
-			opts := &dpb.MethodOptions{}
-			if err := proto.SetExtension(opts, annotations.E_Http, test.httpRule); err != nil {
-				t.Fatalf("Failed to set google.api.http annotation.")
-			}
-
-			// Create a minimal service with a AIP-134 Update method
-			// (or with a different method, in the "Irrelevant" case).
-			service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName,
-				builder.RpcTypeMessage(builder.NewMessage("UpdateBookRequest"), false),
-				builder.RpcTypeMessage(builder.NewMessage("Book"), false),
-			).SetOptions(opts)).Build()
-			if err != nil {
-				t.Fatalf("Could not build %s method.", test.methodName)
-			}
-
-			// Run the method, ensure we get what we expect.
-			problems := httpMethod.Lint(service.GetFile())
-			if test.msg == "" && len(problems) > 0 {
-				t.Errorf("Got %v, expected no problems.", problems)
-			} else if test.msg != "" && !strings.Contains(problems[0].Message, test.msg) {
-				t.Errorf("Got %q, expected message containing %q", problems[0].Message, test.msg)
-			}
-		})
-	}
-}
-
-func TestHttpBody(t *testing.T) {
-	tests := []struct {
-		testName   string
-		body       string
-		methodName string
-		msg        string
-	}{
-		{"Valid", "book", "UpdateBook", ""},
-		{"InvalidFoo", "foo", "UpdateBook", "HTTP body"},
-		{"InvalidStar", "*", "UpdateBook", "HTTP body"},
-		{"InvalidEmpty", "", "UpdateBook", "HTTP body"},
-		{"Irrelevant", "*", "AcquireBook", ""},
-	}
-
-	for _, test := range tests {
-		t.Run(test.testName, func(t *testing.T) {
-			// Create a MethodOptions with the annotation set.
-			opts := &dpb.MethodOptions{}
-			httpRule := &annotations.HttpRule{
-				Pattern: &annotations.HttpRule_Patch{
-					Patch: "/v1/{book.name=publishers/*/books/*}",
-				},
-				Body: test.body,
-			}
-			if err := proto.SetExtension(opts, annotations.E_Http, httpRule); err != nil {
-				t.Fatalf("Failed to set google.api.http annotation.")
-			}
-
-			// Create a minimal service with a AIP-134 Update method
-			// (or with a different method, in the "Irrelevant" case).
-			service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName,
-				builder.RpcTypeMessage(builder.NewMessage("UpdateBookRequest"), false),
-				builder.RpcTypeMessage(builder.NewMessage("Book"), false),
-			).SetOptions(opts)).Build()
-			if err != nil {
-				t.Fatalf("Could not build %s method.", test.methodName)
-			}
-
-			// Run the method, ensure we get what we expect.
-			problems := httpBody.Lint(service.GetFile())
-			if test.msg == "" && len(problems) > 0 {
-				t.Errorf("Got %v, expected no problems.", problems)
-			} else if test.msg != "" && !strings.Contains(problems[0].Message, test.msg) {
-				t.Errorf("Got %q, expected message containing %q", problems[0].Message, test.msg)
-			}
-		})
-	}
-}
-
-func TestHttpNameField(t *testing.T) {
-	tests := []struct {
-		testName   string
-		uri        string
-		methodName string
-		msg        string
-	}{
-		{"Valid", "/v1/{big_book.name=publishers/*/books/*}",
-			"UpdateBigBook", ""},
-		{"InvalidNoUnderscore", "/v1/{bigbook.name=publishers/*/books/*}",
-			"UpdateBigBook", "`big_book.name` field"},
-		{"InvalidVarNameBook", "/v1/{big_book=publishers/*/books/*}",
-			"UpdateBigBook", "`big_book.name` field"},
-		{"InvalidVarNameName", "/v1/{name=publishers/*/books/*}",
-			"UpdateBigBook", "`big_book.name` field"},
-		{"InvalidVarNameReversed", "/v1/{name.big_book=publishers/*/books/*}",
-			"UpdateBigBook", "`big_book.name` field"},
-		{"NoVarName", "/v1/publishers/*/books/*",
-			"UpdateBigBook", "`big_book.name` field"},
-		{"Irrelevant", "/v1/{book=publishers/*/books/*}",
-			"AcquireBigBook", ""},
-	}
-
-	for _, test := range tests {
-		t.Run(test.testName, func(t *testing.T) {
-			// Create a MethodOptions with the annotation set.
-			opts := &dpb.MethodOptions{}
-			httpRule := &annotations.HttpRule{
-				Pattern: &annotations.HttpRule_Patch{
-					Patch: test.uri,
-				},
-			}
-			if err := proto.SetExtension(opts, annotations.E_Http, httpRule); err != nil {
-				t.Fatalf("Failed to set google.api.http annotation.")
-			}
-
-			// Create a minimal service with a AIP-134 Update method
-			// (or with a different method, in the "Irrelevant" case).
-			service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName,
-				builder.RpcTypeMessage(builder.NewMessage("UpdateBigBookRequest"), false),
-				builder.RpcTypeMessage(builder.NewMessage("BigBook"), false),
-			).SetOptions(opts)).Build()
-			if err != nil {
-				t.Fatalf("Could not build %s method.", test.methodName)
-			}
-
-			// Run the method, ensure we get what we expect.
-			problems := httpNameField.Lint(service.GetFile())
-			if test.msg == "" && len(problems) > 0 {
-				t.Errorf("Got %v, expected no problems.", problems)
-			} else if test.msg != "" && len(problems) == 0 {
-				t.Errorf("Got no problems, expected 1.")
-			} else if test.msg != "" && !strings.Contains(problems[0].Message, test.msg) {
-				t.Errorf("Got %q, expected message containing %q", problems[0].Message, test.msg)
-			}
-		})
-	}
-}
-
-func TestSynonyms(t *testing.T) {
-	tests := []struct {
-		MethodName string
-		problems   testutils.Problems
-	}{
-		{"UpdateBook", testutils.Problems{}},
-		{"PatchBook", testutils.Problems{{Suggestion: "UpdateBook"}}},
-		{"PutBook", testutils.Problems{{Suggestion: "UpdateBook"}}},
-		{"SetBook", testutils.Problems{{Suggestion: "UpdateBook"}}},
-	}
-	for _, test := range tests {
-		t.Run(test.MethodName, func(t *testing.T) {
-			file := testutils.ParseProto3Tmpl(t, `
-				service Library {
-					rpc {{.MethodName}}({{.MethodName}}Request) returns (Book);
-				}
-				message {{.MethodName}}Request {}
-				message Book {}
-			`, test)
-			m := file.GetServices()[0].GetMethods()[0]
-			if diff := test.problems.SetDescriptor(m).Diff(synonyms.Lint(file)); diff != "" {
-				t.Errorf(diff)
-			}
-		})
-	}
-}
diff --git a/rules/aip0134/request_mask_field_test.go b/rules/aip0134/request_mask_field_test.go
new file mode 100644
index 0000000..db425f8
--- /dev/null
+++ b/rules/aip0134/request_mask_field_test.go
@@ -0,0 +1,62 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package aip0134
+
+import (
+	"testing"
+
+	"github.com/googleapis/api-linter/rules/internal/testutils"
+	"github.com/jhump/protoreflect/desc"
+)
+
+func TestRequestMaskField(t *testing.T) {
+	tests := []struct {
+		testName   string
+		Field      string
+		descriptor func(*desc.MessageDescriptor) desc.Descriptor
+		problems   testutils.Problems
+	}{
+		{"Valid", "google.protobuf.FieldMask update_mask = 2;", nil, testutils.Problems{}},
+		{"InvalidMissing", "", nil, testutils.Problems{{Message: "`update_mask` field"}}},
+		{
+			"InvalidType",
+			"string update_mask = 2;",
+			func(m *desc.MessageDescriptor) desc.Descriptor {
+				return m.GetFields()[1]
+			},
+			testutils.Problems{{Message: "google.protobuf.FieldMask"}},
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.testName, func(t *testing.T) {
+			file := testutils.ParseProto3Tmpl(t, `
+				import "google/protobuf/field_mask.proto";
+				message UpdateBookRequest {
+					Book book = 1;
+					{{.Field}}
+				}
+				message Book {}
+			`, test)
+			message := file.GetMessageTypes()[0]
+			var d desc.Descriptor = message
+			if test.descriptor != nil {
+				d = test.descriptor(message)
+			}
+			if diff := test.problems.SetDescriptor(d).Diff(requestMaskField.Lint(file)); diff != "" {
+				t.Errorf(diff)
+			}
+		})
+	}
+}
diff --git a/rules/aip0134/request_message_name_test.go b/rules/aip0134/request_message_name_test.go
new file mode 100644
index 0000000..a5432ab
--- /dev/null
+++ b/rules/aip0134/request_message_name_test.go
@@ -0,0 +1,57 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package aip0134
+
+import (
+	"testing"
+
+	"github.com/googleapis/api-linter/rules/internal/testutils"
+	"github.com/jhump/protoreflect/desc/builder"
+)
+
+func TestRequestMessageName(t *testing.T) {
+	// Set up the testing permutations.
+	tests := []struct {
+		testName       string
+		methodName     string
+		reqMessageName string
+		problems       testutils.Problems
+	}{
+		{"Valid", "UpdateBook", "UpdateBookRequest", testutils.Problems{}},
+		{"Invalid", "UpdateBook", "Book", testutils.Problems{{Suggestion: "UpdateBookRequest"}}},
+		{"Irrelevant", "AcquireBook", "Book", testutils.Problems{}},
+	}
+
+	// Run each test individually.
+	for _, test := range tests {
+		t.Run(test.testName, func(t *testing.T) {
+			// Create a minimal service with a AIP-134 Update method
+			// (or with a different method, in the "Irrelevant" case).
+			service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName,
+				builder.RpcTypeMessage(builder.NewMessage(test.reqMessageName), false),
+				builder.RpcTypeMessage(builder.NewMessage("Book"), false),
+			)).Build()
+			if err != nil {
+				t.Fatalf("Could not build %s method.", test.methodName)
+			}
+
+			// Run the lint rule, and establish that it returns the expected problems.
+			problems := requestMessageName.Lint(service.GetFile())
+			if diff := test.problems.SetDescriptor(service.GetMethods()[0]).Diff(problems); diff != "" {
+				t.Errorf(diff)
+			}
+		})
+	}
+}
diff --git a/rules/aip0134/request_resource_field_test.go b/rules/aip0134/request_resource_field_test.go
new file mode 100644
index 0000000..6f95ca6
--- /dev/null
+++ b/rules/aip0134/request_resource_field_test.go
@@ -0,0 +1,78 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package aip0134
+
+import (
+	"testing"
+
+	"github.com/googleapis/api-linter/rules/internal/testutils"
+	"github.com/jhump/protoreflect/desc"
+)
+
+func TestResourceField(t *testing.T) {
+	// Set up the testing permutations.
+	tests := []struct {
+		testName          string
+		MessageName       string
+		ResourceName      string
+		ResourceFieldName string
+		descriptor        func(*desc.MessageDescriptor) desc.Descriptor
+		problems          testutils.Problems
+	}{
+		{"Valid", "UpdateBookRequest", "Book", "book", nil, testutils.Problems{}},
+		{"ValidTwoWords", "UpdateBigBookRequest", "BigBook", "big_book", nil, testutils.Problems{}},
+		{
+			"InvalidMismatch",
+			"UpdateBookRequest",
+			"Foo",
+			"foo",
+			nil,
+			testutils.Problems{{Message: "has no \"Book\""}}},
+		{
+			"InvalidFieldName",
+			"UpdateBookRequest",
+			"Book",
+			"big_book",
+			func(m *desc.MessageDescriptor) desc.Descriptor {
+				return m.GetFields()[0]
+			},
+			testutils.Problems{{Suggestion: "book"}},
+		},
+	}
+
+	// Run each test individually.
+	for _, test := range tests {
+		t.Run(test.testName, func(t *testing.T) {
+			file := testutils.ParseProto3Tmpl(t, `
+				import "google/protobuf/field_mask.proto";
+				message {{.MessageName}} {
+					{{.ResourceName}} {{.ResourceFieldName}} = 1;
+					google.protobuf.FieldMask update_mask = 2;
+				}
+				message {{.ResourceName}} {}
+			`, test)
+			var d desc.Descriptor = file.GetMessageTypes()[0]
+			if test.descriptor != nil {
+				d = test.descriptor(d.(*desc.MessageDescriptor))
+			}
+
+			// Run the lint rule, and establish that it returns the correct problems.
+			problems := resourceField.Lint(file)
+			if diff := test.problems.SetDescriptor(d).Diff(problems); diff != "" {
+				t.Errorf(diff)
+			}
+		})
+	}
+}
diff --git a/rules/aip0134/request_unknown_fields_test.go b/rules/aip0134/request_unknown_fields_test.go
new file mode 100644
index 0000000..5db42d2
--- /dev/null
+++ b/rules/aip0134/request_unknown_fields_test.go
@@ -0,0 +1,72 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package aip0134
+
+import (
+	"testing"
+
+	"github.com/googleapis/api-linter/rules/internal/testutils"
+	"github.com/jhump/protoreflect/desc"
+	"github.com/jhump/protoreflect/desc/builder"
+	fpb "google.golang.org/genproto/protobuf/field_mask"
+)
+
+func TestUnknownFields(t *testing.T) {
+	// Get the correct message type for google.protobuf.FieldMask.
+	fieldMask, err := desc.LoadMessageDescriptorForMessage(&fpb.FieldMask{})
+	if err != nil {
+		t.Fatalf("Unable to load the field mask message.")
+	}
+
+	// Set up the testing permutations.
+	tests := []struct {
+		testName    string
+		messageName string
+		fieldName   string
+		fieldType   *builder.FieldType
+		problems    testutils.Problems
+	}{
+		// Use BigBook instead of Book to test correct casing logic
+		{"UpdateMask", "UpdateBigBookRequest", "update_mask",
+			builder.FieldTypeImportedMessage(fieldMask), testutils.Problems{}},
+		{"Invalid", "UpdateBigBookRequest", "application_id",
+			builder.FieldTypeString(), testutils.Problems{{Message: "Unexpected field"}}},
+		{"InvalidCasing", "UpdateBigBookRequest", "bigbook",
+			builder.FieldTypeString(), testutils.Problems{{Message: "Unexpected field"}}},
+		{"Irrelevant", "AcquireBigBookRequest", "application_id",
+			builder.FieldTypeString(), testutils.Problems{}},
+	}
+
+	// Run each test individually.
+	for _, test := range tests {
+		t.Run(test.testName, func(t *testing.T) {
+			// Create an appropriate message descriptor.
+			message, err := builder.NewMessage(test.messageName).AddField(
+				builder.NewField("big_book", builder.FieldTypeMessage(builder.NewMessage("BigBook"))),
+			).AddField(
+				builder.NewField(test.fieldName, test.fieldType),
+			).Build()
+			if err != nil {
+				t.Fatalf("Could not build UpdateBookRequest message.")
+			}
+
+			// Run the lint rule, and establish that it returns the correct problems.
+			wantProblems := test.problems.SetDescriptor(message.FindFieldByName(test.fieldName))
+			gotProblems := unknownFields.Lint(message.GetFile())
+			if diff := wantProblems.Diff(gotProblems); diff != "" {
+				t.Errorf(diff)
+			}
+		})
+	}
+}
diff --git a/rules/aip0134/response_message_name_test.go b/rules/aip0134/response_message_name_test.go
new file mode 100644
index 0000000..f28939f
--- /dev/null
+++ b/rules/aip0134/response_message_name_test.go
@@ -0,0 +1,69 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package aip0134
+
+import (
+	"testing"
+
+	"github.com/googleapis/api-linter/rules/internal/testutils"
+)
+
+func TestResponseMessageName(t *testing.T) {
+	// Set up the testing permutations.
+	tests := []struct {
+		testName     string
+		MethodName   string
+		RespTypeName string
+		LRO          bool
+		problems     testutils.Problems
+	}{
+		{"ValidResource", "UpdateBook", "Book", false, testutils.Problems{}},
+		{"ValidLRO", "UpdateBook", "Book", true, testutils.Problems{}},
+		{"Invalid", "UpdateBook", "UpdateBookResponse", false, testutils.Problems{{Suggestion: "Book"}}},
+		{"InvalidLRO", "UpdateBook", "UpdateBookResponse", true, testutils.Problems{{Suggestion: "Book"}}},
+		{"Irrelevant", "MutateBook", "MutateBookResponse", false, testutils.Problems{}},
+	}
+
+	// Run each test individually.
+	for _, test := range tests {
+		t.Run(test.testName, func(t *testing.T) {
+			// Create a minimal service with a AIP-134 Update method
+			file := testutils.ParseProto3Tmpl(t, `
+				import "google/longrunning/operations.proto";
+				service Library {
+					rpc {{.MethodName}}({{.MethodName}}Request)
+							returns ({{ if .LRO }}google.longrunning.Operation{{ else }}{{ .RespTypeName }}{{ end }}) {
+						{{ if .LRO -}}
+						option (google.longrunning.operation_info) = {
+							response_type: "{{.RespTypeName}}"
+							metadata_type: "{{.MethodName}}Metadata"
+						};
+						{{ end -}}
+					}
+				}
+				message {{.MethodName}}Request {}
+				message {{.RespTypeName}} {}
+			`, test)
+
+			// Run the lint rule, and establish that it returns the correct
+			// number of problems.
+			problems := responseMessageName.Lint(file)
+			method := file.GetServices()[0].GetMethods()[0]
+			if diff := test.problems.SetDescriptor(method).Diff(problems); diff != "" {
+				t.Errorf(diff)
+			}
+		})
+	}
+}
diff --git a/rules/aip0134/synonyms_test.go b/rules/aip0134/synonyms_test.go
new file mode 100644
index 0000000..b860529
--- /dev/null
+++ b/rules/aip0134/synonyms_test.go
@@ -0,0 +1,48 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package aip0134
+
+import (
+	"testing"
+
+	"github.com/googleapis/api-linter/rules/internal/testutils"
+)
+
+func TestSynonyms(t *testing.T) {
+	tests := []struct {
+		MethodName string
+		problems   testutils.Problems
+	}{
+		{"UpdateBook", testutils.Problems{}},
+		{"PatchBook", testutils.Problems{{Suggestion: "UpdateBook"}}},
+		{"PutBook", testutils.Problems{{Suggestion: "UpdateBook"}}},
+		{"SetBook", testutils.Problems{{Suggestion: "UpdateBook"}}},
+	}
+	for _, test := range tests {
+		t.Run(test.MethodName, func(t *testing.T) {
+			file := testutils.ParseProto3Tmpl(t, `
+				service Library {
+					rpc {{.MethodName}}({{.MethodName}}Request) returns (Book);
+				}
+				message {{.MethodName}}Request {}
+				message Book {}
+			`, test)
+			m := file.GetServices()[0].GetMethods()[0]
+			if diff := test.problems.SetDescriptor(m).Diff(synonyms.Lint(file)); diff != "" {
+				t.Errorf(diff)
+			}
+		})
+	}
+}