File: test_request_payload.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (457 lines) | stat: -rw-r--r-- 19,222 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Test support library for request payloads.

#ifndef CHROME_BROWSER_POLICY_MESSAGING_LAYER_UTIL_TEST_REQUEST_PAYLOAD_H_
#define CHROME_BROWSER_POLICY_MESSAGING_LAYER_UTIL_TEST_REQUEST_PAYLOAD_H_

#include <list>
#include <ostream>
#include <sstream>
#include <string>
#include <string_view>
#include <type_traits>

#include "base/containers/flat_map.h"
#include "base/values.h"

#include "testing/gmock/include/gmock/gmock.h"

namespace reporting {

using ::testing::AllOfArray;
using ::testing::Matcher;
using ::testing::MatchResultListener;

// Matcher interface for each request validity matcher. A request validity
// matcher is a matcher that verifies one aspect of the general validity of a
// request JSON object.
class RequestValidityMatcherInterface {
 public:
  using is_gtest_matcher = void;
  virtual ~RequestValidityMatcherInterface() = default;
  virtual bool MatchAndExplain(const base::Value::Dict& arg,
                               MatchResultListener* listener) const = 0;
  virtual void DescribeTo(std::ostream* os) const = 0;
  virtual void DescribeNegationTo(std::ostream* os) const = 0;
  // Name of this matcher.
  virtual std::string Name() const = 0;
};

// attachEncryptionSettings must be of bool type and true.
class AttachEncryptionSettingsMatcher : public RequestValidityMatcherInterface {
 public:
  bool MatchAndExplain(const base::Value::Dict& arg,
                       MatchResultListener* listener) const override;
  void DescribeTo(std::ostream* os) const override;
  void DescribeNegationTo(std::ostream* os) const override;
  std::string Name() const override;
};

// attachEncryptionSettings must be absent.
class NoAttachEncryptionSettingsMatcher
    : public RequestValidityMatcherInterface {
 public:
  bool MatchAndExplain(const base::Value::Dict& arg,
                       MatchResultListener* listener) const override;
  void DescribeTo(std::ostream* os) const override;
  void DescribeNegationTo(std::ostream* os) const override;
  std::string Name() const override;
};

// ConfigurationFileVersion must be of bool type and true.
class ConfigurationFileVersionMatcher : public RequestValidityMatcherInterface {
 public:
  bool MatchAndExplain(const base::Value::Dict& arg,
                       MatchResultListener* listener) const override;
  void DescribeTo(std::ostream* os) const override;
  void DescribeNegationTo(std::ostream* os) const override;
  std::string Name() const override;
};

// ConfigurationFileVersion must be absent.
class NoConfigurationFileVersionMatcher
    : public RequestValidityMatcherInterface {
 public:
  bool MatchAndExplain(const base::Value::Dict& arg,
                       MatchResultListener* listener) const override;
  void DescribeTo(std::ostream* os) const override;
  void DescribeNegationTo(std::ostream* os) const override;
  std::string Name() const override;
};

// source must be of string type.
class SourceMatcher : public RequestValidityMatcherInterface {
 public:
  bool MatchAndExplain(const base::Value::Dict& arg,
                       MatchResultListener* listener) const override;
  void DescribeTo(std::ostream* os) const override;
  void DescribeNegationTo(std::ostream* os) const override;
  std::string Name() const override;
};

// source must be absent.
class NoSourceMatcher : public RequestValidityMatcherInterface {
 public:
  bool MatchAndExplain(const base::Value::Dict& arg,
                       MatchResultListener* listener) const override;
  void DescribeTo(std::ostream* os) const override;
  void DescribeNegationTo(std::ostream* os) const override;
  std::string Name() const override;
};

// encryptedRecord must be a list. This matcher is recommended to be applies
// before verifying the details of any record (e.g., via |RecordMatcher|) to
// generate more readable error messages.
class EncryptedRecordMatcher : public RequestValidityMatcherInterface {
 public:
  bool MatchAndExplain(const base::Value::Dict& arg,
                       MatchResultListener* listener) const override;
  void DescribeTo(std::ostream* os) const override;
  void DescribeNegationTo(std::ostream* os) const override;
  std::string Name() const override;
};

// requestId must be a hexadecimal number represented as a string.
class RequestIdMatcher : public RequestValidityMatcherInterface {
 public:
  bool MatchAndExplain(const base::Value::Dict& arg,
                       MatchResultListener* listener) const override;
  void DescribeTo(std::ostream* os) const override;
  void DescribeNegationTo(std::ostream* os) const override;
  std::string Name() const override;
};

// Base class of all matchers that verify one aspect of a record.
class RecordMatcher : public RequestValidityMatcherInterface {
 public:
  enum class Mode : char {
    // The default. Match the content of each record in a full request payload
    // in "encryptedRecord". The argument is a dict that represents a full
    // request payload. This is typically switched on if the test case is
    // verifying a full request payload.
    FullRequest = 'f',
    // Directly match the content of a single record assuming no
    // "encryptedRecord" key wrapping it. The argument is a dict that contains
    // the record. This is typically switched on if the test case is verifying a
    // single record.
    RecordOnly = 'r'
  };
  bool MatchAndExplain(const base::Value::Dict& arg,
                       MatchResultListener* listener) const final;
  // Match and explain the given record.
  virtual bool MatchAndExplainRecord(const base::Value::Dict& arg,
                                     MatchResultListener* listener) const = 0;
  // Change mode. See the doc of |Mode| above.
  RecordMatcher& SetMode(Mode mode);

  // A helper function that calls |SetMode| above and casts the type back to the
  // derived class.
  template <class DerivedRecordMatcher>
  static DerivedRecordMatcher& SetMode(DerivedRecordMatcher&& record_matcher,
                                       Mode mode) {
    static_assert(std::is_base_of<RecordMatcher, DerivedRecordMatcher>::value,
                  "record_matcher must be of type RecordMatcher.");
    return static_cast<DerivedRecordMatcher&>(record_matcher.SetMode(mode));
  }

 private:
  Mode mode_ = Mode::FullRequest;
};

// Verify the encryptedWrappedRecord field of each record.
class EncryptedWrappedRecordRecordMatcher : public RecordMatcher {
 public:
  bool MatchAndExplainRecord(const base::Value::Dict& arg,
                             MatchResultListener* listener) const override;
  void DescribeTo(std::ostream* os) const override;
  void DescribeNegationTo(std::ostream* os) const override;
  std::string Name() const override;
};

// Verify the absence of the encryptedWrappedRecord field of each record.
class NoEncryptedWrappedRecordRecordMatcher : public RecordMatcher {
 public:
  bool MatchAndExplainRecord(const base::Value::Dict& arg,
                             MatchResultListener* listener) const override;
  void DescribeTo(std::ostream* os) const override;
  void DescribeNegationTo(std::ostream* os) const override;
  std::string Name() const override;
};

// Verify the sequenceInformation field of each record.
class SequenceInformationRecordMatcher : public RecordMatcher {
 public:
  bool MatchAndExplainRecord(const base::Value::Dict& arg,
                             MatchResultListener* listener) const override;
  void DescribeTo(std::ostream* os) const override;
  void DescribeNegationTo(std::ostream* os) const override;
  std::string Name() const override;
};

// Verify the compressionInformation field of each record.
class CompressionInformationMatcher : public RecordMatcher {
 public:
  bool MatchAndExplainRecord(const base::Value::Dict& arg,
                             MatchResultListener* listener) const override;
  void DescribeTo(std::ostream* os) const override;
  void DescribeNegationTo(std::ostream* os) const override;
  std::string Name() const override;
};

// Build a matcher that can be used to verify the general validity of a
// request matcher while accommodating the variety of requirements (e.g.,
// some verification must be loosen because the request is intentionally
// malformed for a particular test case). To use this class, call
// CreateDataUpload() or CreateEmpty() to create an instance. Adapt matchers by
// calling AppendMatcher() or RemoveMatcher().
//
// For the document of what response payload should look like, search for
// "{{{Note}}} ERP Request Payload Overview" in the codebase.
template <class T = base::Value::Dict>
class RequestValidityMatcherBuilder {
 public:
  // We can't support copy because after copying, matcher_list_t's iterators in
  // matcher_index_ would point to the elements in the wrong
  // RequestValidityMatcherBuilder instance.
  RequestValidityMatcherBuilder(const RequestValidityMatcherBuilder<T>&) =
      delete;
  RequestValidityMatcherBuilder<T>& operator=(
      const RequestValidityMatcherBuilder<T>&) = delete;
  RequestValidityMatcherBuilder(RequestValidityMatcherBuilder<T>&&) = default;
  RequestValidityMatcherBuilder<T>& operator=(
      RequestValidityMatcherBuilder<T>&&) = default;

  // These creator functions are helpful because they are common starting point
  // of a combination of matchers and are friendly to be adapted slightly for
  // some test cases.

  // Creates and returns a |RequestValidityMatcherBuilder| instance that
  // contains no matchers.
  static RequestValidityMatcherBuilder<T> CreateEmpty() {
    return RequestValidityMatcherBuilder<T>();
  }

  // Creates and returns a |RequestValidityMatcherBuilder| instance that
  // contains a matcher that is suited for verifying a data upload request.
  static RequestValidityMatcherBuilder<T> CreateDataUpload() {
    // We need to call std::move here because AppendMatcher returns an lvalue
    // reference. It's important to move the object here because the iterators
    // in matcher_index_ would lose validity otherwise. (If a std::list object
    // is moved, its existing iterators remain valid and point to the elements
    // in this new object. But if it is copied and the old object is destroyed,
    // the existing iterators become invalid.)
    return std::move(RequestValidityMatcherBuilder<T>::CreateEmpty()
                         .AppendMatcher(RequestIdMatcher())
                         .AppendMatcher(EncryptedRecordMatcher())
                         .AppendMatcher(EncryptedWrappedRecordRecordMatcher())
                         .AppendMatcher(SequenceInformationRecordMatcher()));
  }

  // Creates and returns a |RequestValidityMatcherBuilder| instance that
  // contains a matcher that is suited for verifying an encryption key-request
  // upload request. If need_key is false, the matcher will ensure the request
  // does not request an encryption key.
  static RequestValidityMatcherBuilder<T> CreateEncryptionKeyRequestUpload(
      bool need_key) {
    auto builder = RequestValidityMatcherBuilder<T>::CreateEmpty();
    builder.AppendMatcher(RequestIdMatcher());
    if (need_key) {
      builder.AppendMatcher(AttachEncryptionSettingsMatcher());
    } else {
      builder.AppendMatcher(NoAttachEncryptionSettingsMatcher());
    }
    return builder;
  }

  // Creates and returns a |RequestValidityMatcherBuilder| instance that
  // contains a matcher that is suited for verifying a configuration file
  // request. If request_config_file is false the matcher will ensure the
  // request does not request the configuration file.
  static RequestValidityMatcherBuilder<T> CreateConfigurationFileRequestUpload(
      bool request_config_file) {
    auto builder = RequestValidityMatcherBuilder<T>::CreateEmpty();
    builder.AppendMatcher(RequestIdMatcher());
    if (request_config_file) {
      builder.AppendMatcher(ConfigurationFileVersionMatcher());
    } else {
      builder.AppendMatcher(NoConfigurationFileVersionMatcher());
    }
    return builder;
  }

  // Creates and returns a |RequestValidityMatcherBuilder| instance that
  // contains a matcher that is suited for verifying a client automated test
  // request. If client_automated_test is false the matcher will ensure the
  // request does not include the field source.
  static RequestValidityMatcherBuilder<T> CreateSourceRequestUpload(
      bool client_automated_test) {
    auto builder = RequestValidityMatcherBuilder<T>::CreateEmpty();
    builder.AppendMatcher(RequestIdMatcher());
    if (client_automated_test) {
      builder.AppendMatcher(SourceMatcher());
    } else {
      builder.AppendMatcher(NoSourceMatcher());
    }
    return builder;
  }

  // Creates and returns a |RequestValidityMatcherBuilder| instance that
  // contains a matcher that is suited for verifying a single record.
  static RequestValidityMatcherBuilder<T> CreateRecord() {
    return std::move(RequestValidityMatcherBuilder<T>::CreateEmpty()
                         .AppendMatcher(RecordMatcher::SetMode(
                             EncryptedWrappedRecordRecordMatcher(),
                             RecordMatcher::Mode::RecordOnly))
                         .AppendMatcher(RecordMatcher::SetMode(
                             SequenceInformationRecordMatcher(),
                             RecordMatcher::Mode::RecordOnly)));
  }

  // Creates and returns a |RequestValidityMatcherBuilder| instance that
  // contains a matcher that is suited for verifying a gap upload request.
  static RequestValidityMatcherBuilder<T> CreateGapUpload() {
    // A gap upload is a data upload with no encryptedWrappedRecord.
    return std::move(
        RequestValidityMatcherBuilder<T>::CreateDataUpload()
            .RemoveMatcher("encrypted-wrapped-record-record-matcher")
            .AppendMatcher(NoEncryptedWrappedRecordRecordMatcher()));
  }

  // Builds and returns the |Matcher<T>| object.
  [[nodiscard]] Matcher<T> Build() const { return AllOfArray(matchers_); }

  // Append a matcher.
  template <class RequestValidityMatcher>
  RequestValidityMatcherBuilder<T>& AppendMatcher(
      const RequestValidityMatcher& matcher) {
    static_assert(std::is_base_of<RequestValidityMatcherInterface,
                                  RequestValidityMatcher>::value,
                  "matcher must be of type RequestValidityMatcherInterface.");
    matchers_.emplace_back(matcher);
    matcher_index_.emplace(matcher.Name(), std::prev(matchers_.cend()));
    return *this;
  }

  // Remove a matcher.
  RequestValidityMatcherBuilder<T>& RemoveMatcher(std::string_view name) {
    auto matcher_it = matcher_index_.find(name);
    EXPECT_NE(matcher_it, matcher_index_.end())
        << "Matcher \"" << name << "\" not found.";
    matchers_.erase(matcher_it->second);
    matcher_index_.erase(matcher_it);
    return *this;
  }

 private:
  // List of matchers.
  using matcher_list_t = std::list<Matcher<T>>;
  matcher_list_t matchers_{};
  // A name to matcher mapping.
  base::flat_map<std::string, typename matcher_list_t::const_iterator>
      matcher_index_{};

  RequestValidityMatcherBuilder() = default;
};

class RequestContainingRecordMatcher {
 public:
  using is_gtest_matcher = void;

  explicit RequestContainingRecordMatcher(std::string_view matched_record_json);
  bool MatchAndExplain(const base::Value::Dict& arg,
                       MatchResultListener* os) const;
  void DescribeTo(std::ostream* os) const;
  void DescribeNegationTo(std::ostream* os) const;

 private:
  const std::string matched_record_json_;

  // Determine if |sub| is a sub-dictionary of |super|. That means, whether
  // |super| contains all keys of |sub| and the values corresponding to each of
  // |sub|'s keys equal. This method does not call itself recursively on values
  // that are dictionaries.
  static bool IsSubDict(const base::Value::Dict& sub,
                        const base::Value::Dict& super);
};

// The following matcher functions templated because we expect the tested
// request comes in different forms, including their referenceness (gtest need
// the matcher type to also match references to some extent). As long as the
// type can be cast to a |base::Value::Dict| object, this matcher should work.

// Match a data upload request that is valid. This matcher is intended to be
// called for most tested data upload requests to verify whether the request is
// valid on some basic fronts, such as containing an "encryptedRecord" key, etc.
//
// To enable or skip some part of the validity checks (e.g., because your test
// case intentionally creates a malformed request), instead of using this
// wrapper, you must call
//
//     RequestValidityMatcherBuilder<>::CreateDataUpload()
//            .AppendMatcher(...)
//            .RemoveMatcher(...)
//            ...
//            .Build()
template <class T = base::Value::Dict>
Matcher<T> IsDataUploadRequestValid() {
  return RequestValidityMatcherBuilder<T>::CreateDataUpload().Build();
}

// Match an encryption key-request upload request that is valid. If need_key is
// false, this matcher will ensure the request does not request an encryption
// key.
template <class T = base::Value::Dict>
Matcher<T> IsEncryptionKeyRequestUploadRequestValid(bool need_key = true) {
  return RequestValidityMatcherBuilder<T>::CreateEncryptionKeyRequestUpload(
             need_key)
      .Build();
}

// Match a configuration file request upload request that is valid. If
// request_config_file is false, this matcher will ensure the request does not
// request a configuration file.
template <class T = base::Value::Dict>
Matcher<T> IsConfigurationFileRequestUploadRequestValid(
    bool request_config_file = false) {
  return RequestValidityMatcherBuilder<T>::CreateConfigurationFileRequestUpload(
             request_config_file)
      .Build();
}

// Match a source upload request that is valid.
template <class T = base::Value::Dict>
Matcher<T> IsSourceRequestUploadRequestValid(
    bool client_automated_test = false) {
  return RequestValidityMatcherBuilder<T>::CreateSourceRequestUpload(
             client_automated_test)
      .Build();
}

// Match a gap upload request that is valid.
template <class T = base::Value::Dict>
Matcher<T> IsGapUploadRequestValid() {
  return RequestValidityMatcherBuilder<T>::CreateGapUpload().Build();
}

// Match a single record within a payload that is valid.
template <class T = base::Value::Dict>
Matcher<T> IsRecordValid() {
  return RequestValidityMatcherBuilder<T>::CreateRecord().Build();
}

// Match a request that contains the given record |matched_record_json|. The
// match will be successful as long as any record in the request contains
// |matched_record_json| as a sub-dictionary -- they are not required to equal.
// In this way, you can specify only part of the record of interest (e.g., omit
// "encryptedWrappedRecord").
template <class T = base::Value::Dict>
Matcher<T> DoesRequestContainRecord(std::string_view matched_record_json) {
  return RequestContainingRecordMatcher(matched_record_json);
}

}  // namespace reporting

#endif  // CHROME_BROWSER_POLICY_MESSAGING_LAYER_UTIL_TEST_REQUEST_PAYLOAD_H_