couchbase 3.4.3 → 3.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/ext/couchbase/CMakeLists.txt +22 -1
  4. data/ext/couchbase/core/bucket.cxx +183 -152
  5. data/ext/couchbase/core/bucket.hxx +17 -4
  6. data/ext/couchbase/core/cluster.hxx +41 -13
  7. data/ext/couchbase/core/cluster_options.hxx +3 -0
  8. data/ext/couchbase/core/crud_component.cxx +51 -22
  9. data/ext/couchbase/core/error_context/key_value.cxx +2 -1
  10. data/ext/couchbase/core/error_context/key_value.hxx +10 -12
  11. data/ext/couchbase/core/impl/build_deferred_query_indexes.cxx +115 -50
  12. data/ext/couchbase/core/impl/cluster.cxx +6 -0
  13. data/ext/couchbase/core/impl/create_bucket.cxx +158 -0
  14. data/ext/couchbase/core/impl/create_collection.cxx +83 -0
  15. data/ext/couchbase/core/impl/create_query_index.cxx +172 -59
  16. data/ext/couchbase/core/impl/create_scope.cxx +69 -0
  17. data/ext/couchbase/core/impl/dns_srv_tracker.cxx +2 -1
  18. data/ext/couchbase/core/impl/drop_bucket.cxx +66 -0
  19. data/ext/couchbase/core/impl/drop_collection.cxx +76 -0
  20. data/ext/couchbase/core/impl/drop_query_index.cxx +138 -59
  21. data/ext/couchbase/core/impl/drop_scope.cxx +68 -0
  22. data/ext/couchbase/core/impl/flush_bucket.cxx +66 -0
  23. data/ext/couchbase/core/impl/get_all_buckets.cxx +178 -0
  24. data/ext/couchbase/core/impl/get_all_query_indexes.cxx +67 -37
  25. data/ext/couchbase/core/impl/get_all_scopes.cxx +94 -0
  26. data/ext/couchbase/core/impl/get_bucket.cxx +168 -0
  27. data/ext/couchbase/core/impl/internal_manager_error_context.cxx +113 -0
  28. data/ext/couchbase/core/impl/internal_manager_error_context.hxx +60 -0
  29. data/ext/couchbase/core/impl/key_value_error_category.cxx +2 -4
  30. data/ext/couchbase/core/impl/key_value_error_context.cxx +98 -0
  31. data/ext/couchbase/core/impl/lookup_in.cxx +1 -0
  32. data/ext/couchbase/core/impl/lookup_in_all_replicas.cxx +178 -0
  33. data/ext/couchbase/core/impl/lookup_in_all_replicas.hxx +80 -0
  34. data/ext/couchbase/core/impl/lookup_in_any_replica.cxx +169 -0
  35. data/ext/couchbase/core/impl/lookup_in_any_replica.hxx +75 -0
  36. data/ext/couchbase/core/impl/lookup_in_replica.cxx +104 -0
  37. data/ext/couchbase/core/impl/lookup_in_replica.hxx +67 -0
  38. data/ext/couchbase/core/impl/manager_error_context.cxx +100 -0
  39. data/ext/couchbase/core/impl/query.cxx +1 -0
  40. data/ext/couchbase/core/impl/query_error_context.cxx +75 -0
  41. data/ext/couchbase/core/impl/update_bucket.cxx +133 -0
  42. data/ext/couchbase/core/impl/update_collection.cxx +83 -0
  43. data/ext/couchbase/core/impl/watch_query_indexes.cxx +53 -29
  44. data/ext/couchbase/core/io/dns_client.cxx +111 -40
  45. data/ext/couchbase/core/io/dns_config.cxx +5 -4
  46. data/ext/couchbase/core/io/http_session.hxx +24 -1
  47. data/ext/couchbase/core/io/mcbp_command.hxx +9 -2
  48. data/ext/couchbase/core/io/mcbp_session.cxx +80 -43
  49. data/ext/couchbase/core/io/mcbp_session.hxx +4 -3
  50. data/ext/couchbase/core/logger/custom_rotating_file_sink.cxx +1 -1
  51. data/ext/couchbase/core/logger/logger.cxx +80 -20
  52. data/ext/couchbase/core/logger/logger.hxx +31 -0
  53. data/ext/couchbase/core/management/bucket_settings.hxx +8 -5
  54. data/ext/couchbase/core/management/bucket_settings_json.hxx +12 -2
  55. data/ext/couchbase/core/meta/features.hxx +42 -0
  56. data/ext/couchbase/core/operations/document_lookup_in.cxx +8 -1
  57. data/ext/couchbase/core/operations/document_lookup_in_all_replicas.hxx +192 -0
  58. data/ext/couchbase/core/operations/document_lookup_in_any_replica.hxx +188 -0
  59. data/ext/couchbase/core/operations/document_query.cxx +11 -0
  60. data/ext/couchbase/core/operations/document_query.hxx +1 -0
  61. data/ext/couchbase/core/operations/management/CMakeLists.txt +1 -0
  62. data/ext/couchbase/core/operations/management/bucket_create.cxx +30 -9
  63. data/ext/couchbase/core/operations/management/bucket_update.cxx +27 -6
  64. data/ext/couchbase/core/operations/management/collection_create.cxx +5 -1
  65. data/ext/couchbase/core/operations/management/collection_create.hxx +1 -0
  66. data/ext/couchbase/core/operations/management/collection_update.cxx +87 -0
  67. data/ext/couchbase/core/operations/management/collection_update.hxx +54 -0
  68. data/ext/couchbase/core/operations/management/collections.hxx +1 -0
  69. data/ext/couchbase/core/operations.hxx +2 -0
  70. data/ext/couchbase/core/origin.cxx +270 -0
  71. data/ext/couchbase/core/origin.hxx +2 -0
  72. data/ext/couchbase/core/protocol/client_response.hxx +1 -0
  73. data/ext/couchbase/core/protocol/cmd_hello.hxx +1 -0
  74. data/ext/couchbase/core/protocol/cmd_lookup_in_replica.cxx +107 -0
  75. data/ext/couchbase/core/protocol/cmd_lookup_in_replica.hxx +137 -0
  76. data/ext/couchbase/core/protocol/hello_feature.hxx +6 -0
  77. data/ext/couchbase/core/protocol/hello_feature_fmt.hxx +3 -0
  78. data/ext/couchbase/core/protocol/status.cxx +2 -2
  79. data/ext/couchbase/core/range_scan_options.cxx +3 -27
  80. data/ext/couchbase/core/range_scan_options.hxx +13 -17
  81. data/ext/couchbase/core/range_scan_orchestrator.cxx +388 -170
  82. data/ext/couchbase/core/range_scan_orchestrator.hxx +13 -2
  83. data/ext/couchbase/core/range_scan_orchestrator_options.hxx +5 -3
  84. data/ext/couchbase/core/scan_options.hxx +0 -19
  85. data/ext/couchbase/core/scan_result.cxx +19 -5
  86. data/ext/couchbase/core/scan_result.hxx +5 -2
  87. data/ext/couchbase/core/timeout_defaults.hxx +3 -4
  88. data/ext/couchbase/core/topology/capabilities.hxx +4 -0
  89. data/ext/couchbase/core/topology/capabilities_fmt.hxx +11 -0
  90. data/ext/couchbase/core/topology/collections_manifest.hxx +2 -0
  91. data/ext/couchbase/core/topology/collections_manifest_fmt.hxx +1 -1
  92. data/ext/couchbase/core/topology/collections_manifest_json.hxx +3 -0
  93. data/ext/couchbase/core/topology/configuration.hxx +20 -0
  94. data/ext/couchbase/core/topology/configuration_json.hxx +8 -1
  95. data/ext/couchbase/core/utils/connection_string.cxx +62 -47
  96. data/ext/couchbase/core/utils/connection_string.hxx +1 -0
  97. data/ext/couchbase/couchbase/analytics_error_context.hxx +1 -1
  98. data/ext/couchbase/couchbase/behavior_options.hxx +19 -2
  99. data/ext/couchbase/couchbase/bucket.hxx +14 -0
  100. data/ext/couchbase/couchbase/bucket_manager.hxx +135 -0
  101. data/ext/couchbase/couchbase/build_query_index_options.hxx +0 -30
  102. data/ext/couchbase/couchbase/cluster.hxx +14 -0
  103. data/ext/couchbase/couchbase/collection.hxx +111 -0
  104. data/ext/couchbase/couchbase/collection_manager.hxx +160 -0
  105. data/ext/couchbase/couchbase/collection_query_index_manager.hxx +7 -48
  106. data/ext/couchbase/couchbase/create_bucket_options.hxx +41 -0
  107. data/ext/couchbase/couchbase/create_collection_options.hxx +44 -0
  108. data/ext/couchbase/couchbase/create_primary_query_index_options.hxx +0 -29
  109. data/ext/couchbase/couchbase/create_query_index_options.hxx +0 -33
  110. data/ext/couchbase/couchbase/create_scope_options.hxx +41 -0
  111. data/ext/couchbase/couchbase/drop_bucket_options.hxx +41 -0
  112. data/ext/couchbase/couchbase/drop_collection_options.hxx +41 -0
  113. data/ext/couchbase/couchbase/drop_primary_query_index_options.hxx +0 -30
  114. data/ext/couchbase/couchbase/drop_query_index_options.hxx +0 -31
  115. data/ext/couchbase/couchbase/drop_scope_options.hxx +41 -0
  116. data/ext/couchbase/couchbase/error_codes.hxx +1 -2
  117. data/ext/couchbase/couchbase/error_context.hxx +10 -2
  118. data/ext/couchbase/couchbase/flush_bucket_options.hxx +41 -0
  119. data/ext/couchbase/{core/topology/error_map_fmt.hxx → couchbase/fmt/key_value_error_map_attribute.hxx} +21 -21
  120. data/ext/couchbase/couchbase/get_all_buckets_options.hxx +44 -0
  121. data/ext/couchbase/couchbase/get_all_query_indexes_options.hxx +0 -30
  122. data/ext/couchbase/couchbase/get_all_scopes_options.hxx +44 -0
  123. data/ext/couchbase/couchbase/get_and_lock_options.hxx +2 -2
  124. data/ext/couchbase/couchbase/get_and_touch_options.hxx +2 -2
  125. data/ext/couchbase/couchbase/get_bucket_options.hxx +43 -0
  126. data/ext/couchbase/couchbase/get_options.hxx +2 -2
  127. data/ext/couchbase/couchbase/insert_options.hxx +3 -3
  128. data/ext/couchbase/couchbase/key_value_error_context.hxx +7 -2
  129. data/ext/couchbase/couchbase/lookup_in_all_replicas_options.hxx +109 -0
  130. data/ext/couchbase/couchbase/lookup_in_any_replica_options.hxx +101 -0
  131. data/ext/couchbase/couchbase/lookup_in_options.hxx +2 -2
  132. data/ext/couchbase/couchbase/lookup_in_replica_result.hxx +74 -0
  133. data/ext/couchbase/couchbase/lookup_in_result.hxx +26 -0
  134. data/ext/couchbase/couchbase/management/bucket_settings.hxx +119 -0
  135. data/ext/couchbase/couchbase/management/collection_spec.hxx +29 -0
  136. data/ext/couchbase/couchbase/management/scope_spec.hxx +29 -0
  137. data/ext/couchbase/couchbase/manager_error_context.hxx +29 -53
  138. data/ext/couchbase/couchbase/mutate_in_options.hxx +2 -2
  139. data/ext/couchbase/couchbase/query_error_context.hxx +3 -1
  140. data/ext/couchbase/couchbase/query_index_manager.hxx +16 -83
  141. data/ext/couchbase/couchbase/query_options.hxx +18 -0
  142. data/ext/couchbase/couchbase/remove_options.hxx +2 -2
  143. data/ext/couchbase/couchbase/replace_options.hxx +3 -3
  144. data/ext/couchbase/couchbase/security_options.hxx +15 -0
  145. data/ext/couchbase/couchbase/subdocument_error_context.hxx +4 -2
  146. data/ext/couchbase/couchbase/touch_options.hxx +2 -2
  147. data/ext/couchbase/couchbase/unlock_options.hxx +2 -2
  148. data/ext/couchbase/couchbase/update_bucket_options.hxx +41 -0
  149. data/ext/couchbase/couchbase/update_collection_options.hxx +44 -0
  150. data/ext/couchbase/couchbase/upsert_options.hxx +3 -3
  151. data/ext/couchbase/couchbase/watch_query_indexes_options.hxx +0 -31
  152. data/ext/couchbase/test/CMakeLists.txt +1 -0
  153. data/ext/couchbase/test/test_integration_collections.cxx +6 -0
  154. data/ext/couchbase/test/test_integration_crud.cxx +5 -0
  155. data/ext/couchbase/test/test_integration_examples.cxx +137 -1
  156. data/ext/couchbase/test/test_integration_management.cxx +1009 -309
  157. data/ext/couchbase/test/test_integration_query.cxx +19 -7
  158. data/ext/couchbase/test/test_integration_range_scan.cxx +351 -112
  159. data/ext/couchbase/test/test_integration_search.cxx +10 -1
  160. data/ext/couchbase/test/test_integration_subdoc.cxx +721 -7
  161. data/ext/couchbase/test/test_transaction_public_async_api.cxx +13 -12
  162. data/ext/couchbase/test/test_transaction_public_blocking_api.cxx +27 -21
  163. data/ext/couchbase/test/test_unit_connection_string.cxx +29 -0
  164. data/ext/couchbase/test/test_unit_query.cxx +75 -0
  165. data/ext/couchbase.cxx +735 -60
  166. data/ext/revisions.rb +3 -3
  167. data/lib/couchbase/cluster.rb +1 -1
  168. data/lib/couchbase/collection.rb +108 -0
  169. data/lib/couchbase/collection_options.rb +100 -1
  170. data/lib/couchbase/errors.rb +5 -0
  171. data/lib/couchbase/key_value_scan.rb +125 -0
  172. data/lib/couchbase/management/bucket_manager.rb +22 -15
  173. data/lib/couchbase/management/collection_manager.rb +158 -9
  174. data/lib/couchbase/options.rb +151 -0
  175. data/lib/couchbase/scope.rb +1 -1
  176. data/lib/couchbase/utils/time.rb +14 -1
  177. data/lib/couchbase/version.rb +1 -1
  178. metadata +59 -8
  179. data/ext/couchbase/core/impl/collection_query_index_manager.cxx +0 -93
@@ -74,244 +74,366 @@ TEST_CASE("integration: bucket management", "[integration]")
74
74
 
75
75
  SECTION("crud")
76
76
  {
77
- couchbase::core::management::cluster::bucket_settings bucket_settings;
78
- bucket_settings.name = bucket_name;
79
- bucket_settings.ram_quota_mb = 100;
80
- bucket_settings.num_replicas = 1;
81
- bucket_settings.bucket_type = couchbase::core::management::cluster::bucket_type::couchbase;
82
- bucket_settings.eviction_policy = couchbase::core::management::cluster::bucket_eviction_policy::value_only;
83
- bucket_settings.flush_enabled = true;
84
- if (integration.cluster_version().is_enterprise()) {
85
- bucket_settings.max_expiry = 10;
86
- bucket_settings.compression_mode = couchbase::core::management::cluster::bucket_compression::active;
87
- }
88
- bucket_settings.replica_indexes = true;
89
- bucket_settings.conflict_resolution_type = couchbase::core::management::cluster::bucket_conflict_resolution::sequence_number;
90
-
91
- {
92
- couchbase::core::operations::management::bucket_create_request req;
93
- req.bucket = bucket_settings;
94
- auto resp = test::utils::execute(integration.cluster, req);
95
- REQUIRE_SUCCESS(resp.ctx.ec);
96
- }
97
-
98
- {
99
- auto resp = wait_for_bucket_created(integration, bucket_name);
100
- REQUIRE_SUCCESS(resp.ctx.ec);
101
- REQUIRE(bucket_settings.bucket_type == resp.bucket.bucket_type);
102
- REQUIRE(bucket_settings.name == resp.bucket.name);
103
- REQUIRE(Approx(bucket_settings.ram_quota_mb).margin(5) == resp.bucket.ram_quota_mb);
104
- REQUIRE(bucket_settings.num_replicas == resp.bucket.num_replicas);
105
- REQUIRE(bucket_settings.flush_enabled == resp.bucket.flush_enabled);
106
- REQUIRE(bucket_settings.max_expiry == resp.bucket.max_expiry);
107
- REQUIRE(bucket_settings.eviction_policy == resp.bucket.eviction_policy);
108
- REQUIRE(bucket_settings.compression_mode == resp.bucket.compression_mode);
109
- REQUIRE(bucket_settings.replica_indexes == resp.bucket.replica_indexes);
110
- }
111
- std::uint64_t old_quota_mb{ 0 };
112
- {
113
- couchbase::core::operations::management::bucket_get_all_request req{};
114
- auto resp = test::utils::execute(integration.cluster, req);
115
- INFO(resp.ctx.http_body);
116
- REQUIRE_SUCCESS(resp.ctx.ec);
117
- bool found = false;
118
- for (const auto& bucket : resp.buckets) {
119
- if (bucket.name != bucket_name) {
120
- continue;
121
- }
122
- found = true;
123
- REQUIRE(bucket_settings.bucket_type == bucket.bucket_type);
124
- REQUIRE(bucket_settings.name == bucket.name);
125
- REQUIRE(bucket_settings.ram_quota_mb == bucket.ram_quota_mb);
126
- old_quota_mb = bucket_settings.ram_quota_mb;
127
- REQUIRE(bucket_settings.num_replicas == bucket.num_replicas);
128
- REQUIRE(bucket_settings.flush_enabled == bucket.flush_enabled);
129
- REQUIRE(bucket_settings.max_expiry == bucket.max_expiry);
130
- REQUIRE(bucket_settings.eviction_policy == bucket.eviction_policy);
131
- REQUIRE(bucket_settings.compression_mode == bucket.compression_mode);
132
- REQUIRE(bucket_settings.replica_indexes == bucket.replica_indexes);
133
- break;
134
- }
135
- REQUIRE(found);
136
- }
137
-
138
- {
139
- bucket_settings.ram_quota_mb = old_quota_mb + 20;
140
- couchbase::core::operations::management::bucket_update_request req;
141
- req.bucket = bucket_settings;
142
- auto resp = test::utils::execute(integration.cluster, req);
143
- REQUIRE_SUCCESS(resp.ctx.ec);
144
- }
145
-
146
- auto ram_quota_updated = test::utils::wait_until([&integration, &bucket_name, old_quota_mb]() {
147
- couchbase::core::operations::management::bucket_get_request req{ bucket_name };
148
- auto resp = test::utils::execute(integration.cluster, req);
149
- return !resp.ctx.ec && resp.bucket.ram_quota_mb > old_quota_mb;
150
- });
151
- REQUIRE(ram_quota_updated);
152
-
153
- {
154
- couchbase::core::operations::management::bucket_drop_request req{ bucket_name };
155
- auto resp = test::utils::execute(integration.cluster, req);
156
- REQUIRE_SUCCESS(resp.ctx.ec);
157
- }
158
-
159
- {
160
- couchbase::core::operations::management::bucket_get_request req{ bucket_name };
161
- auto resp = retry_on_error(integration, req, {});
162
- REQUIRE(resp.ctx.ec == couchbase::errc::common::bucket_not_found);
163
- }
164
-
165
- {
166
- couchbase::core::operations::management::bucket_get_all_request req;
167
- auto resp = test::utils::execute(integration.cluster, req);
168
- REQUIRE_SUCCESS(resp.ctx.ec);
169
- REQUIRE(!resp.buckets.empty());
170
- auto known_buckets =
171
- std::count_if(resp.buckets.begin(), resp.buckets.end(), [&bucket_name](auto& entry) { return entry.name == bucket_name; });
172
- REQUIRE(known_buckets == 0);
173
- }
174
- }
175
-
176
- SECTION("flush")
177
- {
178
- SECTION("flush item")
77
+ SECTION("core API")
179
78
  {
180
- couchbase::core::document_id id{ bucket_name, "_default", "_default", test::utils::uniq_id("foo") };
181
-
79
+ couchbase::core::management::cluster::bucket_settings bucket_settings;
80
+ bucket_settings.name = bucket_name;
81
+ bucket_settings.ram_quota_mb = 100;
82
+ bucket_settings.num_replicas = 1;
83
+ bucket_settings.bucket_type = couchbase::core::management::cluster::bucket_type::couchbase;
84
+ bucket_settings.eviction_policy = couchbase::core::management::cluster::bucket_eviction_policy::value_only;
85
+ bucket_settings.flush_enabled = true;
86
+ if (integration.cluster_version().is_enterprise()) {
87
+ bucket_settings.max_expiry = 10;
88
+ bucket_settings.compression_mode = couchbase::core::management::cluster::bucket_compression::active;
89
+ }
90
+ bucket_settings.replica_indexes = true;
91
+ bucket_settings.conflict_resolution_type = couchbase::core::management::cluster::bucket_conflict_resolution::sequence_number;
182
92
  {
183
93
  couchbase::core::operations::management::bucket_create_request req;
184
- req.bucket.name = bucket_name;
185
- req.bucket.flush_enabled = true;
94
+ req.bucket = bucket_settings;
186
95
  auto resp = test::utils::execute(integration.cluster, req);
187
96
  REQUIRE_SUCCESS(resp.ctx.ec);
188
97
  }
189
98
 
190
- REQUIRE(test::utils::wait_until_bucket_healthy(integration.cluster, bucket_name));
191
-
192
- test::utils::open_bucket(integration.cluster, bucket_name);
99
+ {
100
+ auto resp = wait_for_bucket_created(integration, bucket_name);
101
+ REQUIRE_SUCCESS(resp.ctx.ec);
102
+ REQUIRE(bucket_settings.bucket_type == resp.bucket.bucket_type);
103
+ REQUIRE(bucket_settings.name == resp.bucket.name);
104
+ REQUIRE(Approx(bucket_settings.ram_quota_mb).margin(5) == resp.bucket.ram_quota_mb);
105
+ REQUIRE(bucket_settings.num_replicas == resp.bucket.num_replicas);
106
+ REQUIRE(bucket_settings.flush_enabled == resp.bucket.flush_enabled);
107
+ REQUIRE(bucket_settings.max_expiry == resp.bucket.max_expiry);
108
+ REQUIRE(bucket_settings.eviction_policy == resp.bucket.eviction_policy);
109
+ REQUIRE(bucket_settings.compression_mode == resp.bucket.compression_mode);
110
+ REQUIRE(bucket_settings.replica_indexes == resp.bucket.replica_indexes);
111
+ }
193
112
 
194
113
  {
195
- const tao::json::value value = {
196
- { "a", 1.0 },
197
- };
198
- couchbase::core::operations::insert_request req{ id, couchbase::core::utils::json::generate_binary(value) };
114
+ couchbase::core::operations::management::bucket_create_request req;
115
+ req.bucket = bucket_settings;
199
116
  auto resp = test::utils::execute(integration.cluster, req);
200
- REQUIRE_SUCCESS(resp.ctx.ec());
117
+ REQUIRE(resp.ctx.ec == couchbase::errc::management::bucket_exists);
201
118
  }
202
119
 
120
+ std::uint64_t old_quota_mb{ 0 };
203
121
  {
204
- couchbase::core::operations::get_request req{ id };
122
+ couchbase::core::operations::management::bucket_get_all_request req{};
205
123
  auto resp = test::utils::execute(integration.cluster, req);
206
- REQUIRE_SUCCESS(resp.ctx.ec());
124
+ INFO(resp.ctx.http_body);
125
+ REQUIRE_SUCCESS(resp.ctx.ec);
126
+ bool found = false;
127
+ for (const auto& bucket : resp.buckets) {
128
+ if (bucket.name != bucket_name) {
129
+ continue;
130
+ }
131
+ found = true;
132
+ REQUIRE(bucket_settings.bucket_type == bucket.bucket_type);
133
+ REQUIRE(bucket_settings.name == bucket.name);
134
+ REQUIRE(bucket_settings.ram_quota_mb == bucket.ram_quota_mb);
135
+ old_quota_mb = bucket_settings.ram_quota_mb;
136
+ REQUIRE(bucket_settings.num_replicas == bucket.num_replicas);
137
+ REQUIRE(bucket_settings.flush_enabled == bucket.flush_enabled);
138
+ REQUIRE(bucket_settings.max_expiry == bucket.max_expiry);
139
+ REQUIRE(bucket_settings.eviction_policy == bucket.eviction_policy);
140
+ REQUIRE(bucket_settings.compression_mode == bucket.compression_mode);
141
+ REQUIRE(bucket_settings.replica_indexes == bucket.replica_indexes);
142
+ break;
143
+ }
144
+ REQUIRE(found);
207
145
  }
208
146
 
209
147
  {
210
- couchbase::core::operations::management::bucket_flush_request req{ bucket_name };
148
+ bucket_settings.ram_quota_mb = old_quota_mb + 20;
149
+ couchbase::core::operations::management::bucket_update_request req;
150
+ req.bucket = bucket_settings;
211
151
  auto resp = test::utils::execute(integration.cluster, req);
212
152
  REQUIRE_SUCCESS(resp.ctx.ec);
213
153
  }
214
154
 
215
- auto flushed = test::utils::wait_until([&integration, id]() {
216
- couchbase::core::operations::get_request req{ id };
155
+ auto ram_quota_updated = test::utils::wait_until([&integration, &bucket_name, old_quota_mb]() {
156
+ couchbase::core::operations::management::bucket_get_request req{ bucket_name };
217
157
  auto resp = test::utils::execute(integration.cluster, req);
218
- return resp.ctx.ec() == couchbase::errc::key_value::document_not_found;
158
+ return !resp.ctx.ec && resp.bucket.ram_quota_mb > old_quota_mb;
219
159
  });
220
- REQUIRE(flushed);
221
- }
222
-
223
- SECTION("no bucket")
224
- {
225
- couchbase::core::operations::management::bucket_flush_request req{ bucket_name };
226
- auto resp = test::utils::execute(integration.cluster, req);
227
- REQUIRE(resp.ctx.ec == couchbase::errc::common::bucket_not_found);
228
- }
160
+ REQUIRE(ram_quota_updated);
229
161
 
230
- SECTION("flush disabled")
231
- {
232
162
  {
233
- couchbase::core::operations::management::bucket_create_request req;
234
- req.bucket.name = bucket_name;
235
- req.bucket.flush_enabled = false;
163
+ couchbase::core::operations::management::bucket_drop_request req{ bucket_name };
236
164
  auto resp = test::utils::execute(integration.cluster, req);
237
165
  REQUIRE_SUCCESS(resp.ctx.ec);
238
166
  }
239
167
 
240
- REQUIRE(test::utils::wait_until_bucket_healthy(integration.cluster, bucket_name));
168
+ {
169
+ couchbase::core::operations::management::bucket_get_request req{ bucket_name };
170
+ auto resp = retry_on_error(integration, req, {});
171
+ REQUIRE(resp.ctx.ec == couchbase::errc::common::bucket_not_found);
172
+ }
241
173
 
242
174
  {
243
- couchbase::core::operations::management::bucket_flush_request req{ bucket_name };
175
+ couchbase::core::operations::management::bucket_get_all_request req;
244
176
  auto resp = test::utils::execute(integration.cluster, req);
245
- REQUIRE(resp.ctx.ec == couchbase::errc::management::bucket_not_flushable);
177
+ REQUIRE_SUCCESS(resp.ctx.ec);
178
+ REQUIRE(!resp.buckets.empty());
179
+ auto known_buckets = std::count_if(
180
+ resp.buckets.begin(), resp.buckets.end(), [&bucket_name](auto& entry) { return entry.name == bucket_name; });
181
+ REQUIRE(known_buckets == 0);
246
182
  }
247
183
  }
248
- }
249
-
250
- if (integration.cluster_version().supports_memcached_buckets()) {
251
- SECTION("memcached")
184
+ SECTION("public API")
252
185
  {
186
+ couchbase::cluster c(integration.cluster);
187
+ couchbase::management::cluster::bucket_settings bucket_settings;
188
+ bucket_settings.name = bucket_name;
189
+ bucket_settings.ram_quota_mb = 100;
190
+ bucket_settings.num_replicas = 1;
191
+ bucket_settings.bucket_type = couchbase::management::cluster::bucket_type::couchbase;
192
+ bucket_settings.eviction_policy = couchbase::management::cluster::bucket_eviction_policy::value_only;
193
+ bucket_settings.flush_enabled = true;
194
+ if (integration.cluster_version().is_enterprise()) {
195
+ bucket_settings.max_expiry = 10;
196
+ bucket_settings.compression_mode = couchbase::management::cluster::bucket_compression::active;
197
+ }
198
+ bucket_settings.replica_indexes = true;
199
+ bucket_settings.conflict_resolution_type = couchbase::management::cluster::bucket_conflict_resolution::sequence_number;
200
+ {
201
+ auto ctx = c.buckets().create_bucket(bucket_settings, {}).get();
202
+ REQUIRE_SUCCESS(ctx.ec());
203
+ }
253
204
  {
254
- couchbase::core::management::cluster::bucket_settings bucket_settings;
255
- bucket_settings.name = bucket_name;
256
- bucket_settings.bucket_type = couchbase::core::management::cluster::bucket_type::memcached;
257
- bucket_settings.num_replicas = 0;
258
- couchbase::core::operations::management::bucket_create_request req{ bucket_settings };
259
- auto resp = test::utils::execute(integration.cluster, req);
260
- REQUIRE_SUCCESS(resp.ctx.ec);
205
+ auto bucket_exists = test::utils::wait_until([&bucket_name, &c]() {
206
+ auto [ctx, bucket] = c.buckets().get_bucket(bucket_name, {}).get();
207
+ return ctx.ec() != couchbase::errc::common::bucket_not_found;
208
+ });
209
+ REQUIRE(bucket_exists);
210
+ auto [ctx, bucket] = c.buckets().get_bucket(bucket_name, {}).get();
211
+ REQUIRE_SUCCESS(ctx.ec());
212
+ REQUIRE(bucket_settings.bucket_type == bucket.bucket_type);
213
+ REQUIRE(bucket_settings.name == bucket.name);
214
+ REQUIRE(Approx(bucket_settings.ram_quota_mb).margin(5) == bucket.ram_quota_mb);
215
+ REQUIRE(bucket_settings.num_replicas == bucket.num_replicas);
216
+ REQUIRE(bucket_settings.flush_enabled == bucket.flush_enabled);
217
+ REQUIRE(bucket_settings.max_expiry == bucket.max_expiry);
218
+ REQUIRE(bucket_settings.eviction_policy == bucket.eviction_policy);
219
+ REQUIRE(bucket_settings.compression_mode == bucket.compression_mode);
220
+ REQUIRE(bucket_settings.replica_indexes == bucket.replica_indexes);
221
+ }
222
+ std::uint64_t old_quota_mb{ 0 };
223
+ {
224
+ auto [ctx, buckets] = c.buckets().get_all_buckets({}).get();
225
+ INFO(ctx.content());
226
+ REQUIRE_SUCCESS(ctx.ec());
227
+ bool found = false;
228
+ for (const auto& bucket : buckets) {
229
+ if (bucket.name != bucket_name) {
230
+ continue;
231
+ }
232
+ found = true;
233
+ REQUIRE(bucket_settings.bucket_type == bucket.bucket_type);
234
+ REQUIRE(bucket_settings.name == bucket.name);
235
+ REQUIRE(bucket_settings.ram_quota_mb == bucket.ram_quota_mb);
236
+ old_quota_mb = bucket_settings.ram_quota_mb;
237
+ REQUIRE(bucket_settings.num_replicas == bucket.num_replicas);
238
+ REQUIRE(bucket_settings.flush_enabled == bucket.flush_enabled);
239
+ REQUIRE(bucket_settings.max_expiry == bucket.max_expiry);
240
+ REQUIRE(bucket_settings.eviction_policy == bucket.eviction_policy);
241
+ REQUIRE(bucket_settings.compression_mode == bucket.compression_mode);
242
+ REQUIRE(bucket_settings.replica_indexes == bucket.replica_indexes);
243
+ break;
244
+ }
245
+ REQUIRE(found);
261
246
  }
262
247
 
263
248
  {
264
- auto resp = wait_for_bucket_created(integration, bucket_name);
265
- REQUIRE_SUCCESS(resp.ctx.ec);
266
- REQUIRE(resp.bucket.bucket_type == couchbase::core::management::cluster::bucket_type::memcached);
249
+ bucket_settings.ram_quota_mb = old_quota_mb + 20;
250
+ auto ctx = c.buckets().update_bucket(bucket_settings, {}).get();
251
+ REQUIRE_SUCCESS(ctx.ec());
252
+ }
253
+ auto ram_quota_updated = test::utils::wait_until([&bucket_name, c, old_quota_mb]() {
254
+ auto [ctx, bucket] = c.buckets().get_bucket(bucket_name, {}).get();
255
+ return !ctx.ec() && bucket.ram_quota_mb > old_quota_mb;
256
+ });
257
+ REQUIRE(ram_quota_updated);
258
+ {
259
+ auto ctx = c.buckets().drop_bucket(bucket_name, {}).get();
260
+ REQUIRE_SUCCESS(ctx.ec());
261
+ }
262
+ {
263
+ auto bucket_not_found = test::utils::wait_until([&bucket_name, c]() {
264
+ auto [ctx, bucket] = c.buckets().get_bucket(bucket_name, {}).get();
265
+ return ctx.ec() == couchbase::errc::common::bucket_not_found;
266
+ });
267
+ REQUIRE(bucket_not_found);
268
+ }
269
+ {
270
+ auto [ctx, buckets] = c.buckets().get_all_buckets({}).get();
271
+ REQUIRE_SUCCESS(ctx.ec());
272
+ REQUIRE(!buckets.empty());
273
+ auto known_buckets =
274
+ std::count_if(buckets.begin(), buckets.end(), [&bucket_name](auto& entry) { return entry.name == bucket_name; });
275
+ REQUIRE(known_buckets == 0);
267
276
  }
268
277
  }
269
278
  }
270
279
 
271
- SECTION("ephemeral")
280
+ SECTION("flush")
272
281
  {
273
- couchbase::core::management::cluster::bucket_settings bucket_settings;
274
- bucket_settings.name = bucket_name;
275
- bucket_settings.bucket_type = couchbase::core::management::cluster::bucket_type::ephemeral;
276
-
277
- SECTION("default eviction")
282
+ SECTION("core api")
278
283
  {
284
+ SECTION("flush item")
279
285
  {
280
- couchbase::core::operations::management::bucket_create_request req{ bucket_settings };
286
+ couchbase::core::document_id id{ bucket_name, "_default", "_default", test::utils::uniq_id("foo") };
287
+
288
+ {
289
+ couchbase::core::operations::management::bucket_create_request req;
290
+ req.bucket.name = bucket_name;
291
+ req.bucket.flush_enabled = true;
292
+ auto resp = test::utils::execute(integration.cluster, req);
293
+ REQUIRE_SUCCESS(resp.ctx.ec);
294
+ }
295
+
296
+ REQUIRE(test::utils::wait_until_bucket_healthy(integration.cluster, bucket_name));
297
+
298
+ test::utils::open_bucket(integration.cluster, bucket_name);
299
+
300
+ {
301
+ const tao::json::value value = {
302
+ { "a", 1.0 },
303
+ };
304
+ couchbase::core::operations::insert_request req{ id, couchbase::core::utils::json::generate_binary(value) };
305
+ auto resp = test::utils::execute(integration.cluster, req);
306
+ REQUIRE_SUCCESS(resp.ctx.ec());
307
+ }
308
+
309
+ {
310
+ couchbase::core::operations::get_request req{ id };
311
+ auto resp = test::utils::execute(integration.cluster, req);
312
+ REQUIRE_SUCCESS(resp.ctx.ec());
313
+ }
314
+
315
+ {
316
+ couchbase::core::operations::management::bucket_flush_request req{ bucket_name };
317
+ auto resp = test::utils::execute(integration.cluster, req);
318
+ REQUIRE_SUCCESS(resp.ctx.ec);
319
+ }
320
+
321
+ auto flushed = test::utils::wait_until([&integration, id]() {
322
+ couchbase::core::operations::get_request req{ id };
323
+ auto resp = test::utils::execute(integration.cluster, req);
324
+ return resp.ctx.ec() == couchbase::errc::key_value::document_not_found;
325
+ });
326
+ REQUIRE(flushed);
327
+ }
328
+ SECTION("no bucket")
329
+ {
330
+ couchbase::core::operations::management::bucket_flush_request req{ bucket_name };
281
331
  auto resp = test::utils::execute(integration.cluster, req);
282
- REQUIRE_SUCCESS(resp.ctx.ec);
332
+ REQUIRE(resp.ctx.ec == couchbase::errc::common::bucket_not_found);
283
333
  }
284
334
 
335
+ SECTION("flush disabled")
285
336
  {
286
- auto resp = wait_for_bucket_created(integration, bucket_name);
287
- REQUIRE_SUCCESS(resp.ctx.ec);
288
- REQUIRE(resp.bucket.bucket_type == couchbase::core::management::cluster::bucket_type::ephemeral);
289
- REQUIRE(resp.bucket.eviction_policy == couchbase::core::management::cluster::bucket_eviction_policy::no_eviction);
337
+ {
338
+ couchbase::core::operations::management::bucket_create_request req;
339
+ req.bucket.name = bucket_name;
340
+ req.bucket.flush_enabled = false;
341
+ auto resp = test::utils::execute(integration.cluster, req);
342
+ REQUIRE_SUCCESS(resp.ctx.ec);
343
+ }
344
+
345
+ REQUIRE(test::utils::wait_until_bucket_healthy(integration.cluster, bucket_name));
346
+
347
+ {
348
+ couchbase::core::operations::management::bucket_flush_request req{ bucket_name };
349
+ auto resp = test::utils::execute(integration.cluster, req);
350
+ REQUIRE(resp.ctx.ec == couchbase::errc::management::bucket_not_flushable);
351
+ }
290
352
  }
291
353
  }
292
-
293
- SECTION("nru eviction")
354
+ SECTION("public API")
294
355
  {
356
+ couchbase::cluster c(integration.cluster);
357
+ SECTION("flush item")
295
358
  {
296
- bucket_settings.eviction_policy = couchbase::core::management::cluster::bucket_eviction_policy::not_recently_used;
297
- couchbase::core::operations::management::bucket_create_request req{ bucket_settings };
298
- auto resp = test::utils::execute(integration.cluster, req);
299
- REQUIRE_SUCCESS(resp.ctx.ec);
359
+ auto id = test::utils::uniq_id("foo");
360
+
361
+ {
362
+ couchbase::management::cluster::bucket_settings bucket_settings;
363
+ bucket_settings.name = bucket_name;
364
+ bucket_settings.flush_enabled = true;
365
+ auto ctx = c.buckets().create_bucket(bucket_settings, {}).get();
366
+ REQUIRE_SUCCESS(ctx.ec());
367
+ }
368
+
369
+ REQUIRE(test::utils::wait_until_bucket_healthy(integration.cluster, bucket_name));
370
+
371
+ test::utils::open_bucket(integration.cluster, bucket_name);
372
+
373
+ auto default_coll = c.bucket(bucket_name).default_collection();
374
+ {
375
+ const tao::json::value value = {
376
+ { "a", 1.0 },
377
+ };
378
+
379
+ auto [ctx, resp] = default_coll.insert(id, value, {}).get();
380
+ REQUIRE_SUCCESS(ctx.ec());
381
+ }
382
+ {
383
+ auto [ctx, resp] = default_coll.get(id, {}).get();
384
+ REQUIRE_SUCCESS(ctx.ec());
385
+ }
386
+ {
387
+ auto ctx = c.buckets().flush_bucket(bucket_name, {}).get();
388
+ REQUIRE_SUCCESS(ctx.ec());
389
+ }
390
+ auto flushed = test::utils::wait_until([id, default_coll]() {
391
+ auto [ctx, resp] = default_coll.get(id, {}).get();
392
+ return ctx.ec() == couchbase::errc::key_value::document_not_found;
393
+ });
394
+ REQUIRE(flushed);
300
395
  }
301
396
 
397
+ SECTION("no bucket")
302
398
  {
303
- auto resp = wait_for_bucket_created(integration, bucket_name);
304
- REQUIRE_SUCCESS(resp.ctx.ec);
305
- REQUIRE(resp.bucket.bucket_type == couchbase::core::management::cluster::bucket_type::ephemeral);
306
- REQUIRE(resp.bucket.eviction_policy == couchbase::core::management::cluster::bucket_eviction_policy::not_recently_used);
399
+ auto ctx = c.buckets().flush_bucket(bucket_name, {}).get();
400
+ REQUIRE(ctx.ec() == couchbase::errc::common::bucket_not_found);
401
+ }
402
+
403
+ SECTION("flush disabled")
404
+ {
405
+ {
406
+ couchbase::management::cluster::bucket_settings bucket_settings;
407
+ bucket_settings.name = bucket_name;
408
+ bucket_settings.flush_enabled = false;
409
+ auto ctx = c.buckets().create_bucket(bucket_settings, {}).get();
410
+ REQUIRE_SUCCESS(ctx.ec());
411
+ }
412
+
413
+ auto bucket_exists = test::utils::wait_until([&bucket_name, c]() {
414
+ auto [ctx, bucket] = c.buckets().get_bucket(bucket_name, {}).get();
415
+ return ctx.ec() != couchbase::errc::common::bucket_not_found;
416
+ });
417
+ REQUIRE(bucket_exists);
418
+
419
+ {
420
+ auto ctx = c.buckets().flush_bucket(bucket_name, {}).get();
421
+ REQUIRE(ctx.ec() == couchbase::errc::management::bucket_not_flushable);
422
+ }
307
423
  }
308
424
  }
425
+ }
309
426
 
310
- if (integration.cluster_version().supports_storage_backend()) {
311
- SECTION("storage backend")
427
+ if (integration.cluster_version().supports_memcached_buckets()) {
428
+ SECTION("memcached")
429
+ {
430
+ SECTION("core api")
312
431
  {
313
432
  {
314
- bucket_settings.storage_backend = couchbase::core::management::cluster::bucket_storage_backend::couchstore;
433
+ couchbase::core::management::cluster::bucket_settings bucket_settings;
434
+ bucket_settings.name = bucket_name;
435
+ bucket_settings.bucket_type = couchbase::core::management::cluster::bucket_type::memcached;
436
+ bucket_settings.num_replicas = 0;
315
437
  couchbase::core::operations::management::bucket_create_request req{ bucket_settings };
316
438
  auto resp = test::utils::execute(integration.cluster, req);
317
439
  REQUIRE_SUCCESS(resp.ctx.ec);
@@ -320,57 +442,78 @@ TEST_CASE("integration: bucket management", "[integration]")
320
442
  {
321
443
  auto resp = wait_for_bucket_created(integration, bucket_name);
322
444
  REQUIRE_SUCCESS(resp.ctx.ec);
323
- REQUIRE(resp.bucket.bucket_type == couchbase::core::management::cluster::bucket_type::ephemeral);
324
- REQUIRE(resp.bucket.storage_backend == couchbase::core::management::cluster::bucket_storage_backend::unknown);
445
+ REQUIRE(resp.bucket.bucket_type == couchbase::core::management::cluster::bucket_type::memcached);
446
+ }
447
+ }
448
+ SECTION("public api")
449
+ {
450
+ couchbase::cluster c(integration.cluster);
451
+ {
452
+ couchbase::management::cluster::bucket_settings bucket_settings;
453
+ bucket_settings.name = bucket_name;
454
+ bucket_settings.bucket_type = couchbase::management::cluster::bucket_type::memcached;
455
+ bucket_settings.num_replicas = 0;
456
+ auto ctx = c.buckets().create_bucket(bucket_settings, {}).get();
457
+ REQUIRE_SUCCESS(ctx.ec());
458
+ }
459
+
460
+ {
461
+ auto bucket_exists = test::utils::wait_until([&bucket_name, &c]() {
462
+ auto [ctx, bucket] = c.buckets().get_bucket(bucket_name, {}).get();
463
+ return ctx.ec() != couchbase::errc::common::bucket_not_found;
464
+ });
465
+ REQUIRE(bucket_exists);
466
+ auto [ctx, bucket] = c.buckets().get_bucket(bucket_name, {}).get();
467
+ REQUIRE_SUCCESS(ctx.ec());
468
+ REQUIRE(bucket.bucket_type == couchbase::management::cluster::bucket_type::memcached);
325
469
  }
326
470
  }
327
471
  }
328
472
  }
329
473
 
330
- SECTION("couchbase")
474
+ SECTION("ephemeral")
331
475
  {
332
- couchbase::core::management::cluster::bucket_settings bucket_settings;
333
- bucket_settings.name = bucket_name;
334
- bucket_settings.bucket_type = couchbase::core::management::cluster::bucket_type::couchbase;
335
-
336
- SECTION("default eviction")
476
+ SECTION("core api")
337
477
  {
338
- {
339
-
340
- couchbase::core::operations::management::bucket_create_request req{ bucket_settings };
341
- auto resp = test::utils::execute(integration.cluster, req);
342
- REQUIRE_SUCCESS(resp.ctx.ec);
343
- }
478
+ couchbase::core::management::cluster::bucket_settings bucket_settings;
479
+ bucket_settings.name = bucket_name;
480
+ bucket_settings.bucket_type = couchbase::core::management::cluster::bucket_type::ephemeral;
344
481
 
482
+ SECTION("default eviction")
345
483
  {
346
- auto resp = wait_for_bucket_created(integration, bucket_name);
347
- REQUIRE_SUCCESS(resp.ctx.ec);
348
- REQUIRE(resp.bucket.bucket_type == couchbase::core::management::cluster::bucket_type::couchbase);
349
- REQUIRE(resp.bucket.eviction_policy == couchbase::core::management::cluster::bucket_eviction_policy::value_only);
350
- }
351
- }
484
+ {
485
+ couchbase::core::operations::management::bucket_create_request req{ bucket_settings };
486
+ auto resp = test::utils::execute(integration.cluster, req);
487
+ REQUIRE_SUCCESS(resp.ctx.ec);
488
+ }
352
489
 
353
- SECTION("full eviction")
354
- {
355
- {
356
- bucket_settings.eviction_policy = couchbase::core::management::cluster::bucket_eviction_policy::full;
357
- couchbase::core::operations::management::bucket_create_request req{ bucket_settings };
358
- auto resp = test::utils::execute(integration.cluster, req);
359
- REQUIRE_SUCCESS(resp.ctx.ec);
490
+ {
491
+ auto resp = wait_for_bucket_created(integration, bucket_name);
492
+ REQUIRE_SUCCESS(resp.ctx.ec);
493
+ REQUIRE(resp.bucket.bucket_type == couchbase::core::management::cluster::bucket_type::ephemeral);
494
+ REQUIRE(resp.bucket.eviction_policy == couchbase::core::management::cluster::bucket_eviction_policy::no_eviction);
495
+ }
360
496
  }
361
497
 
498
+ SECTION("nru eviction")
362
499
  {
363
- auto resp = wait_for_bucket_created(integration, bucket_name);
364
- REQUIRE_SUCCESS(resp.ctx.ec);
365
- REQUIRE(resp.bucket.bucket_type == couchbase::core::management::cluster::bucket_type::couchbase);
366
- REQUIRE(resp.bucket.eviction_policy == couchbase::core::management::cluster::bucket_eviction_policy::full);
500
+ {
501
+ bucket_settings.eviction_policy = couchbase::core::management::cluster::bucket_eviction_policy::not_recently_used;
502
+ couchbase::core::operations::management::bucket_create_request req{ bucket_settings };
503
+ auto resp = test::utils::execute(integration.cluster, req);
504
+ REQUIRE_SUCCESS(resp.ctx.ec);
505
+ }
506
+
507
+ {
508
+ auto resp = wait_for_bucket_created(integration, bucket_name);
509
+ REQUIRE_SUCCESS(resp.ctx.ec);
510
+ REQUIRE(resp.bucket.bucket_type == couchbase::core::management::cluster::bucket_type::ephemeral);
511
+ REQUIRE(resp.bucket.eviction_policy == couchbase::core::management::cluster::bucket_eviction_policy::not_recently_used);
512
+ }
367
513
  }
368
- }
369
514
 
370
- if (integration.cluster_version().supports_storage_backend()) {
371
- SECTION("storage backend")
372
- {
373
- SECTION("couchstore")
515
+ if (integration.cluster_version().supports_storage_backend()) {
516
+ SECTION("storage backend")
374
517
  {
375
518
  {
376
519
  bucket_settings.storage_backend = couchbase::core::management::cluster::bucket_storage_backend::couchstore;
@@ -382,52 +525,95 @@ TEST_CASE("integration: bucket management", "[integration]")
382
525
  {
383
526
  auto resp = wait_for_bucket_created(integration, bucket_name);
384
527
  REQUIRE_SUCCESS(resp.ctx.ec);
385
- REQUIRE(resp.bucket.bucket_type == couchbase::core::management::cluster::bucket_type::couchbase);
386
- REQUIRE(resp.bucket.storage_backend == couchbase::core::management::cluster::bucket_storage_backend::couchstore);
528
+ REQUIRE(resp.bucket.bucket_type == couchbase::core::management::cluster::bucket_type::ephemeral);
529
+ REQUIRE(resp.bucket.storage_backend == couchbase::core::management::cluster::bucket_storage_backend::unknown);
387
530
  }
388
531
  }
532
+ }
533
+ }
534
+ SECTION("public api")
535
+ {
536
+ couchbase::cluster c(integration.cluster);
537
+ couchbase::management::cluster::bucket_settings bucket_settings;
538
+ bucket_settings.name = bucket_name;
539
+ bucket_settings.bucket_type = couchbase::management::cluster::bucket_type::ephemeral;
540
+
541
+ SECTION("default eviction")
542
+ {
543
+ {
544
+ auto ctx = c.buckets().create_bucket(bucket_settings, {}).get();
545
+ REQUIRE_SUCCESS(ctx.ec());
546
+ }
547
+
548
+ {
549
+ auto bucket_exists = test::utils::wait_until([&bucket_name, c]() {
550
+ auto [ctx, bucket] = c.buckets().get_bucket(bucket_name, {}).get();
551
+ return ctx.ec() != couchbase::errc::common::bucket_not_found;
552
+ });
553
+ REQUIRE(bucket_exists);
554
+ auto [ctx, bucket] = c.buckets().get_bucket(bucket_name, {}).get();
555
+ REQUIRE_SUCCESS(ctx.ec());
556
+ REQUIRE(bucket.bucket_type == couchbase::management::cluster::bucket_type::ephemeral);
557
+ REQUIRE(bucket.eviction_policy == couchbase::management::cluster::bucket_eviction_policy::no_eviction);
558
+ }
559
+ }
560
+
561
+ SECTION("nru eviction")
562
+ {
563
+ {
564
+ bucket_settings.eviction_policy = couchbase::management::cluster::bucket_eviction_policy::not_recently_used;
565
+ auto ctx = c.buckets().create_bucket(bucket_settings, {}).get();
566
+ REQUIRE_SUCCESS(ctx.ec());
567
+ }
389
568
 
390
- SECTION("magma")
569
+ {
570
+ auto bucket_exists = test::utils::wait_until([&bucket_name, c]() {
571
+ auto [ctx, bucket] = c.buckets().get_bucket(bucket_name, {}).get();
572
+ return ctx.ec() != couchbase::errc::common::bucket_not_found;
573
+ });
574
+ REQUIRE(bucket_exists);
575
+ auto [ctx, bucket] = c.buckets().get_bucket(bucket_name, {}).get();
576
+ REQUIRE_SUCCESS(ctx.ec());
577
+ REQUIRE(bucket.bucket_type == couchbase::management::cluster::bucket_type::ephemeral);
578
+ REQUIRE(bucket.eviction_policy == couchbase::management::cluster::bucket_eviction_policy::not_recently_used);
579
+ }
580
+ }
581
+ if (integration.cluster_version().supports_storage_backend()) {
582
+ SECTION("storage backend")
391
583
  {
392
584
  {
393
- bucket_settings.ram_quota_mb = integration.cluster_version().is_neo() ? 1'024 : 256;
394
- bucket_settings.storage_backend = couchbase::core::management::cluster::bucket_storage_backend::magma;
395
- couchbase::core::operations::management::bucket_create_request req{ bucket_settings };
396
- auto resp = test::utils::execute(integration.cluster, req);
397
- REQUIRE_SUCCESS(resp.ctx.ec);
585
+ bucket_settings.storage_backend = couchbase::management::cluster::bucket_storage_backend::couchstore;
586
+ auto ctx = c.buckets().create_bucket(bucket_settings, {}).get();
587
+ REQUIRE_SUCCESS(ctx.ec());
398
588
  }
399
589
 
400
590
  {
401
- auto resp = wait_for_bucket_created(integration, bucket_name);
402
- REQUIRE_SUCCESS(resp.ctx.ec);
403
- REQUIRE(resp.bucket.bucket_type == couchbase::core::management::cluster::bucket_type::couchbase);
404
- REQUIRE(resp.bucket.storage_backend == couchbase::core::management::cluster::bucket_storage_backend::magma);
591
+ auto bucket_exists = test::utils::wait_until([&bucket_name, c]() {
592
+ auto [ctx, bucket] = c.buckets().get_bucket(bucket_name, {}).get();
593
+ return ctx.ec() != couchbase::errc::common::bucket_not_found;
594
+ });
595
+ REQUIRE(bucket_exists);
596
+ auto [ctx, bucket] = c.buckets().get_bucket(bucket_name, {}).get();
597
+ REQUIRE_SUCCESS(ctx.ec());
598
+ REQUIRE(bucket.bucket_type == couchbase::management::cluster::bucket_type::ephemeral);
599
+ REQUIRE(bucket.storage_backend == couchbase::management::cluster::bucket_storage_backend::unknown);
405
600
  }
406
601
  }
407
602
  }
408
603
  }
409
604
  }
410
-
411
- SECTION("update no bucket")
605
+ SECTION("couchbase")
412
606
  {
413
-
414
- couchbase::core::management::cluster::bucket_settings bucket_settings;
415
- bucket_settings.name = bucket_name;
416
- couchbase::core::operations::management::bucket_update_request req;
417
- req.bucket = bucket_settings;
418
- auto resp = test::utils::execute(integration.cluster, req);
419
- REQUIRE(resp.ctx.ec == couchbase::errc::common::bucket_not_found);
420
- }
421
-
422
- if (integration.cluster_version().supports_minimum_durability_level()) {
423
- SECTION("minimum durability level")
607
+ SECTION("core api")
424
608
  {
425
609
  couchbase::core::management::cluster::bucket_settings bucket_settings;
426
610
  bucket_settings.name = bucket_name;
611
+ bucket_settings.bucket_type = couchbase::core::management::cluster::bucket_type::couchbase;
427
612
 
428
- SECTION("default")
613
+ SECTION("default eviction")
429
614
  {
430
615
  {
616
+
431
617
  couchbase::core::operations::management::bucket_create_request req{ bucket_settings };
432
618
  auto resp = test::utils::execute(integration.cluster, req);
433
619
  REQUIRE_SUCCESS(resp.ctx.ec);
@@ -436,15 +622,199 @@ TEST_CASE("integration: bucket management", "[integration]")
436
622
  {
437
623
  auto resp = wait_for_bucket_created(integration, bucket_name);
438
624
  REQUIRE_SUCCESS(resp.ctx.ec);
439
- REQUIRE(resp.bucket.minimum_durability_level == couchbase::durability_level::none);
625
+ REQUIRE(resp.bucket.bucket_type == couchbase::core::management::cluster::bucket_type::couchbase);
626
+ REQUIRE(resp.bucket.eviction_policy == couchbase::core::management::cluster::bucket_eviction_policy::value_only);
440
627
  }
441
628
  }
442
629
 
443
- if (integration.number_of_nodes() >= 2) {
444
- SECTION("majority")
630
+ SECTION("full eviction")
631
+ {
632
+ {
633
+ bucket_settings.eviction_policy = couchbase::core::management::cluster::bucket_eviction_policy::full;
634
+ couchbase::core::operations::management::bucket_create_request req{ bucket_settings };
635
+ auto resp = test::utils::execute(integration.cluster, req);
636
+ REQUIRE_SUCCESS(resp.ctx.ec);
637
+ }
638
+
639
+ {
640
+ auto resp = wait_for_bucket_created(integration, bucket_name);
641
+ REQUIRE_SUCCESS(resp.ctx.ec);
642
+ REQUIRE(resp.bucket.bucket_type == couchbase::core::management::cluster::bucket_type::couchbase);
643
+ REQUIRE(resp.bucket.eviction_policy == couchbase::core::management::cluster::bucket_eviction_policy::full);
644
+ }
645
+ }
646
+
647
+ if (integration.cluster_version().supports_storage_backend()) {
648
+ SECTION("storage backend")
649
+ {
650
+ SECTION("couchstore")
651
+ {
652
+ {
653
+ bucket_settings.storage_backend = couchbase::core::management::cluster::bucket_storage_backend::couchstore;
654
+ couchbase::core::operations::management::bucket_create_request req{ bucket_settings };
655
+ auto resp = test::utils::execute(integration.cluster, req);
656
+ REQUIRE_SUCCESS(resp.ctx.ec);
657
+ }
658
+
659
+ {
660
+ auto resp = wait_for_bucket_created(integration, bucket_name);
661
+ REQUIRE_SUCCESS(resp.ctx.ec);
662
+ REQUIRE(resp.bucket.bucket_type == couchbase::core::management::cluster::bucket_type::couchbase);
663
+ REQUIRE(resp.bucket.storage_backend ==
664
+ couchbase::core::management::cluster::bucket_storage_backend::couchstore);
665
+ }
666
+ }
667
+
668
+ SECTION("magma")
669
+ {
670
+ {
671
+ bucket_settings.ram_quota_mb = integration.cluster_version().is_neo() ? 1'024 : 256;
672
+ bucket_settings.storage_backend = couchbase::core::management::cluster::bucket_storage_backend::magma;
673
+ couchbase::core::operations::management::bucket_create_request req{ bucket_settings };
674
+ auto resp = test::utils::execute(integration.cluster, req);
675
+ REQUIRE_SUCCESS(resp.ctx.ec);
676
+ }
677
+
678
+ {
679
+ auto resp = wait_for_bucket_created(integration, bucket_name);
680
+ REQUIRE_SUCCESS(resp.ctx.ec);
681
+ REQUIRE(resp.bucket.bucket_type == couchbase::core::management::cluster::bucket_type::couchbase);
682
+ REQUIRE(resp.bucket.storage_backend == couchbase::core::management::cluster::bucket_storage_backend::magma);
683
+ }
684
+ }
685
+ }
686
+ }
687
+ }
688
+ SECTION("public api")
689
+ {
690
+ couchbase::cluster c(integration.cluster);
691
+ couchbase::management::cluster::bucket_settings bucket_settings;
692
+ bucket_settings.name = bucket_name;
693
+ bucket_settings.bucket_type = couchbase::management::cluster::bucket_type::couchbase;
694
+
695
+ SECTION("default eviction")
696
+ {
697
+ {
698
+ auto ctx = c.buckets().create_bucket(bucket_settings, {}).get();
699
+ REQUIRE_SUCCESS(ctx.ec());
700
+ }
701
+
702
+ {
703
+ auto bucket_exists = test::utils::wait_until([&bucket_name, c]() {
704
+ auto [ctx, bucket] = c.buckets().get_bucket(bucket_name, {}).get();
705
+ return ctx.ec() != couchbase::errc::common::bucket_not_found;
706
+ });
707
+ REQUIRE(bucket_exists);
708
+ auto [ctx, bucket] = c.buckets().get_bucket(bucket_name, {}).get();
709
+ REQUIRE_SUCCESS(ctx.ec());
710
+ REQUIRE(bucket.bucket_type == couchbase::management::cluster::bucket_type::couchbase);
711
+ REQUIRE(bucket.eviction_policy == couchbase::management::cluster::bucket_eviction_policy::value_only);
712
+ }
713
+ }
714
+
715
+ SECTION("full eviction")
716
+ {
717
+ {
718
+ bucket_settings.eviction_policy = couchbase::management::cluster::bucket_eviction_policy::full;
719
+ auto ctx = c.buckets().create_bucket(bucket_settings, {}).get();
720
+ REQUIRE_SUCCESS(ctx.ec());
721
+ }
722
+
723
+ {
724
+ auto bucket_exists = test::utils::wait_until([&bucket_name, c]() {
725
+ auto [ctx, bucket] = c.buckets().get_bucket(bucket_name, {}).get();
726
+ return ctx.ec() != couchbase::errc::common::bucket_not_found;
727
+ });
728
+ REQUIRE(bucket_exists);
729
+ auto [ctx, bucket] = c.buckets().get_bucket(bucket_name, {}).get();
730
+ REQUIRE_SUCCESS(ctx.ec());
731
+ REQUIRE(bucket.bucket_type == couchbase::management::cluster::bucket_type::couchbase);
732
+ REQUIRE(bucket.eviction_policy == couchbase::management::cluster::bucket_eviction_policy::full);
733
+ }
734
+ }
735
+
736
+ if (integration.cluster_version().supports_storage_backend()) {
737
+ SECTION("storage backend")
738
+ {
739
+ SECTION("couchstore")
740
+ {
741
+ {
742
+ bucket_settings.storage_backend = couchbase::management::cluster::bucket_storage_backend::couchstore;
743
+ auto ctx = c.buckets().create_bucket(bucket_settings, {}).get();
744
+ REQUIRE_SUCCESS(ctx.ec());
745
+ }
746
+
747
+ {
748
+ auto bucket_exists = test::utils::wait_until([&bucket_name, c]() {
749
+ auto [ctx, bucket] = c.buckets().get_bucket(bucket_name, {}).get();
750
+ return ctx.ec() != couchbase::errc::common::bucket_not_found;
751
+ });
752
+ REQUIRE(bucket_exists);
753
+ auto [ctx, bucket] = c.buckets().get_bucket(bucket_name, {}).get();
754
+ REQUIRE_SUCCESS(ctx.ec());
755
+ REQUIRE(bucket.bucket_type == couchbase::management::cluster::bucket_type::couchbase);
756
+ REQUIRE(bucket.storage_backend == couchbase::management::cluster::bucket_storage_backend::couchstore);
757
+ }
758
+ }
759
+
760
+ SECTION("magma")
761
+ {
762
+ {
763
+ bucket_settings.ram_quota_mb = integration.cluster_version().is_neo() ? 1'024 : 256;
764
+ bucket_settings.storage_backend = couchbase::management::cluster::bucket_storage_backend::magma;
765
+ auto ctx = c.buckets().create_bucket(bucket_settings, {}).get();
766
+ REQUIRE_SUCCESS(ctx.ec());
767
+ }
768
+
769
+ {
770
+ auto bucket_exists = test::utils::wait_until([&bucket_name, c]() {
771
+ auto [ctx, bucket] = c.buckets().get_bucket(bucket_name, {}).get();
772
+ return ctx.ec() != couchbase::errc::common::bucket_not_found;
773
+ });
774
+ REQUIRE(bucket_exists);
775
+ auto [ctx, bucket] = c.buckets().get_bucket(bucket_name, {}).get();
776
+ REQUIRE_SUCCESS(ctx.ec());
777
+ REQUIRE(bucket.bucket_type == couchbase::management::cluster::bucket_type::couchbase);
778
+ REQUIRE(bucket.storage_backend == couchbase::management::cluster::bucket_storage_backend::magma);
779
+ }
780
+ }
781
+ }
782
+ }
783
+ }
784
+ }
785
+
786
+ SECTION("update no bucket")
787
+ {
788
+ SECTION("core api")
789
+ {
790
+ couchbase::core::management::cluster::bucket_settings bucket_settings;
791
+ bucket_settings.name = bucket_name;
792
+ couchbase::core::operations::management::bucket_update_request req;
793
+ req.bucket = bucket_settings;
794
+ auto resp = test::utils::execute(integration.cluster, req);
795
+ REQUIRE(resp.ctx.ec == couchbase::errc::common::bucket_not_found);
796
+ }
797
+ SECTION("public api")
798
+ {
799
+ couchbase::cluster c(integration.cluster);
800
+ couchbase::management::cluster::bucket_settings bucket_settings;
801
+ bucket_settings.name = bucket_name;
802
+ auto ctx = c.buckets().update_bucket(bucket_settings, {}).get();
803
+ REQUIRE(ctx.ec() == couchbase::errc::common::bucket_not_found);
804
+ }
805
+ }
806
+
807
+ if (integration.cluster_version().supports_minimum_durability_level()) {
808
+ SECTION("minimum durability level")
809
+ {
810
+ SECTION("core api")
811
+ {
812
+ couchbase::core::management::cluster::bucket_settings bucket_settings;
813
+ bucket_settings.name = bucket_name;
814
+
815
+ SECTION("default")
445
816
  {
446
817
  {
447
- bucket_settings.minimum_durability_level = couchbase::durability_level::majority;
448
818
  couchbase::core::operations::management::bucket_create_request req{ bucket_settings };
449
819
  auto resp = test::utils::execute(integration.cluster, req);
450
820
  REQUIRE_SUCCESS(resp.ctx.ec);
@@ -453,7 +823,70 @@ TEST_CASE("integration: bucket management", "[integration]")
453
823
  {
454
824
  auto resp = wait_for_bucket_created(integration, bucket_name);
455
825
  REQUIRE_SUCCESS(resp.ctx.ec);
456
- REQUIRE(resp.bucket.minimum_durability_level == couchbase::durability_level::majority);
826
+ REQUIRE(resp.bucket.minimum_durability_level == couchbase::durability_level::none);
827
+ }
828
+ }
829
+
830
+ if (integration.number_of_nodes() >= 2) {
831
+ SECTION("majority")
832
+ {
833
+ {
834
+ bucket_settings.minimum_durability_level = couchbase::durability_level::majority;
835
+ couchbase::core::operations::management::bucket_create_request req{ bucket_settings };
836
+ auto resp = test::utils::execute(integration.cluster, req);
837
+ REQUIRE_SUCCESS(resp.ctx.ec);
838
+ }
839
+
840
+ {
841
+ auto resp = wait_for_bucket_created(integration, bucket_name);
842
+ REQUIRE_SUCCESS(resp.ctx.ec);
843
+ REQUIRE(resp.bucket.minimum_durability_level == couchbase::durability_level::majority);
844
+ }
845
+ }
846
+ }
847
+ }
848
+ SECTION("public api")
849
+ {
850
+ couchbase::cluster c(integration.cluster);
851
+ couchbase::management::cluster::bucket_settings bucket_settings;
852
+ bucket_settings.name = bucket_name;
853
+
854
+ SECTION("default")
855
+ {
856
+ {
857
+ auto ctx = c.buckets().create_bucket(bucket_settings, {}).get();
858
+ REQUIRE_SUCCESS(ctx.ec());
859
+ }
860
+ {
861
+ auto bucket_exists = test::utils::wait_until([&bucket_name, c]() {
862
+ auto [ctx, bucket] = c.buckets().get_bucket(bucket_name, {}).get();
863
+ return ctx.ec() != couchbase::errc::common::bucket_not_found;
864
+ });
865
+ REQUIRE(bucket_exists);
866
+ auto [ctx, bucket] = c.buckets().get_bucket(bucket_name, {}).get();
867
+ REQUIRE_SUCCESS(ctx.ec());
868
+ REQUIRE(bucket.minimum_durability_level == couchbase::durability_level::none);
869
+ }
870
+ }
871
+ if (integration.number_of_nodes() >= 2) {
872
+ SECTION("majority")
873
+ {
874
+ {
875
+ bucket_settings.minimum_durability_level = couchbase::durability_level::majority;
876
+ auto ctx = c.buckets().create_bucket(bucket_settings, {}).get();
877
+ REQUIRE_SUCCESS(ctx.ec());
878
+ }
879
+
880
+ {
881
+ auto bucket_exists = test::utils::wait_until([&bucket_name, c]() {
882
+ auto [ctx, bucket] = c.buckets().get_bucket(bucket_name, {}).get();
883
+ return ctx.ec() != couchbase::errc::common::bucket_not_found;
884
+ });
885
+ REQUIRE(bucket_exists);
886
+ auto [ctx, bucket] = c.buckets().get_bucket(bucket_name, {}).get();
887
+ REQUIRE_SUCCESS(ctx.ec());
888
+ REQUIRE(bucket.minimum_durability_level == couchbase::durability_level::majority);
889
+ }
457
890
  }
458
891
  }
459
892
  }
@@ -469,6 +902,86 @@ TEST_CASE("integration: bucket management", "[integration]")
469
902
  }
470
903
  }
471
904
 
905
+ TEST_CASE("integration: bucket management history", "[integration]")
906
+ {
907
+ test::utils::integration_test_guard integration;
908
+
909
+ if (!integration.cluster_version().supports_bucket_management()) {
910
+ SKIP("cluster does not support bucket management");
911
+ }
912
+
913
+ if (!integration.cluster_version().supports_gcccp()) {
914
+ test::utils::open_bucket(integration.cluster, integration.ctx.bucket);
915
+ }
916
+
917
+ if (!integration.cluster_version().supports_bucket_history()) {
918
+ SKIP("cluster does not support bucket history");
919
+ }
920
+
921
+ auto bucket_name = test::utils::uniq_id("bucket");
922
+ auto update_bucket_name = test::utils::uniq_id("bucket");
923
+
924
+ SECTION("create history")
925
+ {
926
+ {
927
+ couchbase::core::management::cluster::bucket_settings bucket_settings;
928
+ bucket_settings.name = bucket_name;
929
+ bucket_settings.ram_quota_mb = integration.cluster_version().is_neo() ? 1'024 : 256;
930
+ bucket_settings.storage_backend = couchbase::core::management::cluster::bucket_storage_backend::magma;
931
+ bucket_settings.history_retention_collection_default = true;
932
+ bucket_settings.history_retention_bytes = 2147483648;
933
+ bucket_settings.history_retention_duration = 13000;
934
+ couchbase::core::operations::management::bucket_create_request req{ bucket_settings };
935
+ auto resp = test::utils::execute(integration.cluster, req);
936
+ REQUIRE_SUCCESS(resp.ctx.ec);
937
+ }
938
+
939
+ {
940
+ auto resp = wait_for_bucket_created(integration, bucket_name);
941
+ REQUIRE_SUCCESS(resp.ctx.ec);
942
+ REQUIRE(resp.bucket.storage_backend == couchbase::core::management::cluster::bucket_storage_backend::magma);
943
+ REQUIRE(resp.bucket.history_retention_collection_default == true);
944
+ REQUIRE(resp.bucket.history_retention_duration == 13000);
945
+ REQUIRE(resp.bucket.history_retention_bytes == 2147483648);
946
+ }
947
+ }
948
+
949
+ SECTION("update history")
950
+ {
951
+ couchbase::core::management::cluster::bucket_settings bucket_settings;
952
+ bucket_settings.ram_quota_mb = integration.cluster_version().is_neo() ? 1'024 : 256;
953
+ bucket_settings.name = update_bucket_name;
954
+ bucket_settings.storage_backend = couchbase::core::management::cluster::bucket_storage_backend::magma;
955
+ {
956
+ couchbase::core::operations::management::bucket_create_request req{ bucket_settings };
957
+ auto resp = test::utils::execute(integration.cluster, req);
958
+ REQUIRE_SUCCESS(resp.ctx.ec);
959
+ auto get_resp = wait_for_bucket_created(integration, update_bucket_name);
960
+ REQUIRE_SUCCESS(get_resp.ctx.ec);
961
+ }
962
+ {
963
+ bucket_settings.history_retention_collection_default = true;
964
+ bucket_settings.history_retention_bytes = 2147483648;
965
+ bucket_settings.history_retention_duration = 13000;
966
+ couchbase::core::operations::management::bucket_update_request req{ bucket_settings };
967
+ auto resp = test::utils::execute(integration.cluster, req);
968
+ REQUIRE_SUCCESS(resp.ctx.ec);
969
+ auto get_resp = wait_for_bucket_created(integration, update_bucket_name);
970
+ REQUIRE_SUCCESS(get_resp.ctx.ec);
971
+ REQUIRE(get_resp.bucket.storage_backend == couchbase::core::management::cluster::bucket_storage_backend::magma);
972
+ REQUIRE(get_resp.bucket.history_retention_collection_default == true);
973
+ REQUIRE(get_resp.bucket.history_retention_duration == 13000);
974
+ REQUIRE(get_resp.bucket.history_retention_bytes == 2147483648);
975
+ }
976
+ }
977
+ {
978
+ couchbase::core::operations::management::bucket_drop_request req{ bucket_name };
979
+ couchbase::core::operations::management::bucket_drop_request update_req{ update_bucket_name };
980
+ test::utils::execute(integration.cluster, req);
981
+ test::utils::execute(integration.cluster, update_req);
982
+ }
983
+ }
984
+
472
985
  std::optional<couchbase::core::topology::collections_manifest::collection>
473
986
  get_collection(std::shared_ptr<couchbase::core::cluster> cluster,
474
987
  const std::string& bucket_name,
@@ -518,7 +1031,208 @@ TEST_CASE("integration: collection management", "[integration]")
518
1031
  auto scope_name = test::utils::uniq_id("scope");
519
1032
  auto collection_name = test::utils::uniq_id("collection");
520
1033
  std::uint32_t max_expiry = 5;
1034
+ SECTION("core api")
1035
+ {
1036
+ {
1037
+ couchbase::core::operations::management::scope_create_request req{ integration.ctx.bucket, scope_name };
1038
+ auto resp = test::utils::execute(integration.cluster, req);
1039
+ REQUIRE_SUCCESS(resp.ctx.ec);
1040
+ auto created = test::utils::wait_until_collection_manifest_propagated(integration.cluster, integration.ctx.bucket, resp.uid);
1041
+ REQUIRE(created);
1042
+ }
1043
+
1044
+ {
1045
+ auto created = test::utils::wait_until([&]() { return scope_exists(integration.cluster, integration.ctx.bucket, scope_name); });
1046
+ REQUIRE(created);
1047
+ }
1048
+
1049
+ {
1050
+ couchbase::core::operations::management::scope_create_request req{ integration.ctx.bucket, scope_name };
1051
+ auto resp = test::utils::execute(integration.cluster, req);
1052
+ REQUIRE(resp.ctx.ec == couchbase::errc::management::scope_exists);
1053
+ }
1054
+
1055
+ {
1056
+ couchbase::core::operations::management::collection_create_request req{ integration.ctx.bucket, scope_name, collection_name };
1057
+ if (integration.cluster_version().is_enterprise()) {
1058
+ req.max_expiry = max_expiry;
1059
+ }
1060
+ auto resp = test::utils::execute(integration.cluster, req);
1061
+ REQUIRE_SUCCESS(resp.ctx.ec);
1062
+ auto created = test::utils::wait_until_collection_manifest_propagated(integration.cluster, integration.ctx.bucket, resp.uid);
1063
+ REQUIRE(created);
1064
+ }
1065
+
1066
+ {
1067
+ couchbase::core::topology::collections_manifest::collection collection;
1068
+ auto created = test::utils::wait_until([&]() {
1069
+ auto coll = get_collection(integration.cluster, integration.ctx.bucket, scope_name, collection_name);
1070
+ if (coll) {
1071
+ collection = *coll;
1072
+ return true;
1073
+ }
1074
+ return false;
1075
+ });
1076
+ REQUIRE(created);
1077
+ if (integration.cluster_version().is_enterprise()) {
1078
+ REQUIRE(collection.max_expiry == max_expiry);
1079
+ }
1080
+ }
1081
+
1082
+ {
1083
+ couchbase::core::operations::management::collection_create_request req{ integration.ctx.bucket, scope_name, collection_name };
1084
+ auto resp = test::utils::execute(integration.cluster, req);
1085
+ REQUIRE(resp.ctx.ec == couchbase::errc::management::collection_exists);
1086
+ }
1087
+ {
1088
+ couchbase::core::operations::management::collection_drop_request req{ integration.ctx.bucket, scope_name, collection_name };
1089
+ auto resp = test::utils::execute(integration.cluster, req);
1090
+ REQUIRE_SUCCESS(resp.ctx.ec);
1091
+ }
1092
+
1093
+ {
1094
+ auto dropped = test::utils::wait_until(
1095
+ [&]() { return !get_collection(integration.cluster, integration.ctx.bucket, scope_name, collection_name).has_value(); });
1096
+ REQUIRE(dropped);
1097
+ }
1098
+
1099
+ {
1100
+ couchbase::core::operations::management::collection_drop_request req{ integration.ctx.bucket, scope_name, collection_name };
1101
+ auto resp = test::utils::execute(integration.cluster, req);
1102
+ REQUIRE(resp.ctx.ec == couchbase::errc::common::collection_not_found);
1103
+ }
1104
+
1105
+ {
1106
+ couchbase::core::operations::management::scope_drop_request req{ integration.ctx.bucket, scope_name };
1107
+ auto resp = test::utils::execute(integration.cluster, req);
1108
+ REQUIRE_SUCCESS(resp.ctx.ec);
1109
+ }
1110
+
1111
+ {
1112
+ auto dropped =
1113
+ test::utils::wait_until([&]() { return !scope_exists(integration.cluster, integration.ctx.bucket, scope_name); });
1114
+ REQUIRE(dropped);
1115
+ }
1116
+
1117
+ {
1118
+ couchbase::core::operations::management::scope_drop_request req{ integration.ctx.bucket, scope_name };
1119
+ auto resp = test::utils::execute(integration.cluster, req);
1120
+ REQUIRE(resp.ctx.ec == couchbase::errc::common::scope_not_found);
1121
+ }
1122
+ }
1123
+ SECTION("public API")
1124
+ {
1125
+ couchbase::cluster c(integration.cluster);
1126
+ auto manager = c.bucket(integration.ctx.bucket).collections();
1127
+ {
1128
+ auto ctx = manager.create_scope(scope_name).get();
1129
+ REQUIRE_SUCCESS(ctx.ec());
1130
+ }
1131
+ {
1132
+ auto scope_exists = test::utils::wait_until([&scope_name, &manager]() {
1133
+ auto [ctx, result] = manager.get_all_scopes().get();
1134
+ if (!ctx.ec()) {
1135
+ for (auto& scope : result) {
1136
+ if (scope.name == scope_name) {
1137
+ return true;
1138
+ }
1139
+ }
1140
+ }
1141
+ return false;
1142
+ });
1143
+ REQUIRE(scope_exists);
1144
+ }
1145
+ {
1146
+ auto ctx = manager.create_scope(scope_name).get();
1147
+ REQUIRE(ctx.ec() == couchbase::errc::management::scope_exists);
1148
+ }
1149
+ {
1150
+ couchbase::create_collection_settings settings{};
1151
+ if (integration.cluster_version().is_enterprise()) {
1152
+ settings.max_expiry = max_expiry;
1153
+ }
1154
+ auto ctx = manager.create_collection(scope_name, collection_name, settings).get();
1155
+ REQUIRE_SUCCESS(ctx.ec());
1156
+ auto created = test::utils::wait_until([&scope_name, &collection_name, &manager]() {
1157
+ auto [get_ctx, result] = manager.get_all_scopes().get();
1158
+ if (!get_ctx.ec()) {
1159
+ for (auto& scope : result) {
1160
+ if (scope.name == scope_name) {
1161
+ for (auto& collection : scope.collections) {
1162
+ if (collection.name == collection_name) {
1163
+ return true;
1164
+ }
1165
+ }
1166
+ }
1167
+ }
1168
+ }
1169
+ return false;
1170
+ });
1171
+ REQUIRE(created);
1172
+ }
1173
+ {
1174
+ auto [ctx, scopes] = manager.get_all_scopes().get();
1175
+ REQUIRE_SUCCESS(ctx.ec());
1176
+ couchbase::management::bucket::collection_spec spec;
1177
+ for (auto& scope : scopes) {
1178
+ if (scope.name == scope_name) {
1179
+ for (auto& collection : scope.collections) {
1180
+ if (collection.name == collection_name) {
1181
+ spec = collection;
1182
+ }
1183
+ }
1184
+ }
1185
+ }
1186
+ if (integration.cluster_version().is_enterprise()) {
1187
+ REQUIRE(spec.max_expiry == max_expiry);
1188
+ }
1189
+ }
1190
+ {
1191
+ couchbase::create_collection_settings settings{};
1192
+ auto ctx = manager.create_collection(scope_name, collection_name, settings).get();
1193
+ REQUIRE(ctx.ec() == couchbase::errc::management::collection_exists);
1194
+ }
1195
+ {
1196
+ auto ctx = manager.drop_collection(scope_name, collection_name).get();
1197
+ REQUIRE_SUCCESS(ctx.ec());
1198
+ }
1199
+ {
1200
+ auto bucket_name = integration.ctx.bucket;
1201
+ auto does_not_exist = test::utils::wait_until([&scope_name, &collection_name, &manager]() {
1202
+ auto ctx = manager.drop_collection(scope_name, collection_name).get();
1203
+ return (ctx.ec() == couchbase::errc::common::collection_not_found);
1204
+ });
1205
+ REQUIRE(does_not_exist);
1206
+ }
1207
+ {
1208
+ auto ctx = manager.drop_scope(scope_name).get();
1209
+ REQUIRE_SUCCESS(ctx.ec());
1210
+ }
1211
+ {
1212
+ auto does_not_exist = test::utils::wait_until([&scope_name, &manager]() {
1213
+ auto ctx = manager.drop_scope(scope_name).get();
1214
+ return (ctx.ec() == couchbase::errc::common::scope_not_found);
1215
+ });
1216
+ REQUIRE(does_not_exist);
1217
+ }
1218
+ }
1219
+ }
1220
+
1221
+ TEST_CASE("integration: collection management bucket dedup", "[integration]")
1222
+ {
1223
+ test::utils::integration_test_guard integration;
1224
+ test::utils::open_bucket(integration.cluster, integration.ctx.bucket);
1225
+
1226
+ if (!integration.cluster_version().supports_collections()) {
1227
+ SKIP("cluster does not support collections");
1228
+ }
1229
+ if (!integration.has_bucket_capability("nonDedupedHistory")) {
1230
+ SKIP("Bucket does not support non deduped history");
1231
+ }
521
1232
 
1233
+ auto bucket_name = test::utils::uniq_id("bucket");
1234
+ auto scope_name = test::utils::uniq_id("scope");
1235
+ auto collection_name = test::utils::uniq_id("collection");
522
1236
  {
523
1237
  couchbase::core::operations::management::scope_create_request req{ integration.ctx.bucket, scope_name };
524
1238
  auto resp = test::utils::execute(integration.cluster, req);
@@ -532,23 +1246,14 @@ TEST_CASE("integration: collection management", "[integration]")
532
1246
  REQUIRE(created);
533
1247
  }
534
1248
 
535
- {
536
- couchbase::core::operations::management::scope_create_request req{ integration.ctx.bucket, scope_name };
537
- auto resp = test::utils::execute(integration.cluster, req);
538
- REQUIRE(resp.ctx.ec == couchbase::errc::management::scope_exists);
539
- }
540
-
541
1249
  {
542
1250
  couchbase::core::operations::management::collection_create_request req{ integration.ctx.bucket, scope_name, collection_name };
543
- if (integration.cluster_version().is_enterprise()) {
544
- req.max_expiry = max_expiry;
545
- }
1251
+ req.history = true;
546
1252
  auto resp = test::utils::execute(integration.cluster, req);
547
1253
  REQUIRE_SUCCESS(resp.ctx.ec);
548
1254
  auto created = test::utils::wait_until_collection_manifest_propagated(integration.cluster, integration.ctx.bucket, resp.uid);
549
1255
  REQUIRE(created);
550
1256
  }
551
-
552
1257
  {
553
1258
  couchbase::core::topology::collections_manifest::collection collection;
554
1259
  auto created = test::utils::wait_until([&]() {
@@ -560,50 +1265,27 @@ TEST_CASE("integration: collection management", "[integration]")
560
1265
  return false;
561
1266
  });
562
1267
  REQUIRE(created);
563
- if (integration.cluster_version().is_enterprise()) {
564
- REQUIRE(collection.max_expiry == max_expiry);
565
- }
566
- }
567
-
568
- {
569
- couchbase::core::operations::management::collection_create_request req{ integration.ctx.bucket, scope_name, collection_name };
570
- auto resp = test::utils::execute(integration.cluster, req);
571
- REQUIRE(resp.ctx.ec == couchbase::errc::management::collection_exists);
572
- }
573
-
574
- {
575
- couchbase::core::operations::management::collection_drop_request req{ integration.ctx.bucket, scope_name, collection_name };
576
- auto resp = test::utils::execute(integration.cluster, req);
577
- REQUIRE_SUCCESS(resp.ctx.ec);
578
- }
579
-
580
- {
581
- auto dropped = test::utils::wait_until(
582
- [&]() { return !get_collection(integration.cluster, integration.ctx.bucket, scope_name, collection_name).has_value(); });
583
- REQUIRE(dropped);
584
- }
585
-
586
- {
587
- couchbase::core::operations::management::collection_drop_request req{ integration.ctx.bucket, scope_name, collection_name };
588
- auto resp = test::utils::execute(integration.cluster, req);
589
- REQUIRE(resp.ctx.ec == couchbase::errc::common::collection_not_found);
1268
+ REQUIRE(collection.history.value());
590
1269
  }
591
-
592
1270
  {
593
- couchbase::core::operations::management::scope_drop_request req{ integration.ctx.bucket, scope_name };
1271
+ couchbase::core::operations::management::collection_update_request req{ integration.ctx.bucket, scope_name, collection_name };
1272
+ req.history = false;
594
1273
  auto resp = test::utils::execute(integration.cluster, req);
595
1274
  REQUIRE_SUCCESS(resp.ctx.ec);
596
1275
  }
597
-
598
- {
599
- auto dropped = test::utils::wait_until([&]() { return !scope_exists(integration.cluster, integration.ctx.bucket, scope_name); });
600
- REQUIRE(dropped);
601
- }
602
-
603
1276
  {
604
- couchbase::core::operations::management::scope_drop_request req{ integration.ctx.bucket, scope_name };
605
- auto resp = test::utils::execute(integration.cluster, req);
606
- REQUIRE(resp.ctx.ec == couchbase::errc::common::scope_not_found);
1277
+ couchbase::core::topology::collections_manifest::collection collection;
1278
+ auto no_history = test::utils::wait_until([&]() {
1279
+ auto coll = get_collection(integration.cluster, integration.ctx.bucket, scope_name, collection_name);
1280
+ if (coll.has_value()) {
1281
+ if (!coll.value().history.value()) {
1282
+ return true;
1283
+ }
1284
+ return false;
1285
+ }
1286
+ return false;
1287
+ });
1288
+ REQUIRE(no_history);
607
1289
  }
608
1290
  }
609
1291
 
@@ -1036,6 +1718,12 @@ TEST_CASE("integration: user management collections roles", "[integration]")
1036
1718
  REQUIRE(resp.user.roles[0].scope == scope_name);
1037
1719
  REQUIRE(resp.user.roles[0].collection == collection_name);
1038
1720
  }
1721
+
1722
+ {
1723
+ couchbase::core::operations::management::scope_drop_request req{ integration.ctx.bucket, scope_name };
1724
+ auto resp = test::utils::execute(integration.cluster, req);
1725
+ REQUIRE_SUCCESS(resp.ctx.ec);
1726
+ }
1039
1727
  }
1040
1728
 
1041
1729
  TEST_CASE("integration: query index management", "[integration]")
@@ -1499,7 +2187,8 @@ TEST_CASE("integration: collections query index management", "[integration]")
1499
2187
  req.collection_name = collection_name;
1500
2188
  req.is_primary = true;
1501
2189
  resp = test::utils::execute(integration.cluster, req);
1502
- return resp.ctx.ec != couchbase::errc::common::bucket_not_found;
2190
+ return resp.ctx.ec != couchbase::errc::common::bucket_not_found &&
2191
+ resp.ctx.ec != couchbase::errc::common::scope_not_found;
1503
2192
  });
1504
2193
  REQUIRE(operation_completed);
1505
2194
  REQUIRE_SUCCESS(resp.ctx.ec);
@@ -1524,7 +2213,7 @@ TEST_CASE("integration: collections query index management", "[integration]")
1524
2213
  bool operation_completed = test::utils::wait_until([&manager, &ec]() {
1525
2214
  auto ctx = manager.create_primary_index({}).get();
1526
2215
  ec = ctx.ec();
1527
- return ctx.ec() != couchbase::errc::common::bucket_not_found;
2216
+ return ec != couchbase::errc::common::bucket_not_found && ec != couchbase::errc::common::scope_not_found;
1528
2217
  });
1529
2218
 
1530
2219
  REQUIRE(operation_completed);
@@ -1554,7 +2243,8 @@ TEST_CASE("integration: collections query index management", "[integration]")
1554
2243
  req.index_name = index_name;
1555
2244
  req.is_primary = true;
1556
2245
  resp = test::utils::execute(integration.cluster, req);
1557
- return resp.ctx.ec != couchbase::errc::common::bucket_not_found;
2246
+ return resp.ctx.ec != couchbase::errc::common::bucket_not_found &&
2247
+ resp.ctx.ec != couchbase::errc::common::scope_not_found;
1558
2248
  });
1559
2249
  REQUIRE(operation_completed);
1560
2250
  REQUIRE_SUCCESS(resp.ctx.ec);
@@ -1628,7 +2318,8 @@ TEST_CASE("integration: collections query index management", "[integration]")
1628
2318
  req.collection_name = collection_name;
1629
2319
  req.fields = { "field" };
1630
2320
  resp = test::utils::execute(integration.cluster, req);
1631
- return resp.ctx.ec != couchbase::errc::common::bucket_not_found;
2321
+ return resp.ctx.ec != couchbase::errc::common::bucket_not_found &&
2322
+ resp.ctx.ec != couchbase::errc::common::scope_not_found;
1632
2323
  });
1633
2324
  REQUIRE(operation_completed);
1634
2325
  REQUIRE_SUCCESS(resp.ctx.ec);
@@ -1781,7 +2472,8 @@ TEST_CASE("integration: collections query index management", "[integration]")
1781
2472
  req.fields = { "field" };
1782
2473
  req.deferred = true;
1783
2474
  resp = test::utils::execute(integration.cluster, req);
1784
- return resp.ctx.ec != couchbase::errc::common::bucket_not_found;
2475
+ return resp.ctx.ec != couchbase::errc::common::bucket_not_found &&
2476
+ resp.ctx.ec != couchbase::errc::common::scope_not_found;
1785
2477
  });
1786
2478
  REQUIRE(operation_completed);
1787
2479
  REQUIRE_SUCCESS(resp.ctx.ec);
@@ -1954,18 +2646,20 @@ TEST_CASE("integration: collections query index management", "[integration]")
1954
2646
  .ec() == couchbase::errc::common::ambiguous_timeout);
1955
2647
  }
1956
2648
  }
1957
- SECTION("watch missing collection")
1958
- {
1959
- SECTION("public API")
1960
- {
1961
- couchbase::cluster c(integration.cluster);
1962
- auto coll = c.bucket(integration.ctx.bucket).scope(scope_name).collection("missing_collection");
1963
- REQUIRE(coll.query_indexes()
1964
- .watch_indexes({ index_name }, couchbase::watch_query_indexes_options().timeout(std::chrono::seconds(5)))
1965
- .get()
1966
- .ec() == couchbase::errc::common::ambiguous_timeout);
1967
- }
1968
- }
2649
+ SECTION("watch missing collection"){ SECTION("public API"){ couchbase::cluster c(integration.cluster);
2650
+ auto coll = c.bucket(integration.ctx.bucket).scope(scope_name).collection("missing_collection");
2651
+ REQUIRE(coll.query_indexes()
2652
+ .watch_indexes({ index_name }, couchbase::watch_query_indexes_options().timeout(std::chrono::seconds(5)))
2653
+ .get()
2654
+ .ec() == couchbase::errc::common::ambiguous_timeout);
2655
+ }
2656
+ }
2657
+
2658
+ {
2659
+ couchbase::core::operations::management::scope_drop_request req{ integration.ctx.bucket, scope_name };
2660
+ auto resp = test::utils::execute(integration.cluster, req);
2661
+ REQUIRE_SUCCESS(resp.ctx.ec);
2662
+ }
1969
2663
  }
1970
2664
 
1971
2665
  TEST_CASE("integration: analytics index management", "[integration]")
@@ -2597,6 +3291,12 @@ TEST_CASE("integration: analytics external link management", "[integration]")
2597
3291
  run_azure_link_test(integration, dataverse_name, link_name);
2598
3292
  }
2599
3293
  }
3294
+
3295
+ {
3296
+ couchbase::core::operations::management::scope_drop_request req{ integration.ctx.bucket, scope_name };
3297
+ auto resp = test::utils::execute(integration.cluster, req);
3298
+ REQUIRE_SUCCESS(resp.ctx.ec);
3299
+ }
2600
3300
  }
2601
3301
  }
2602
3302
  }
@@ -2662,7 +3362,7 @@ TEST_CASE("integration: search index management", "[integration]")
2662
3362
  if (integration.cluster_version().is_serverless_config_profile()) {
2663
3363
  index.plan_params_json = serverless_plan_params;
2664
3364
  }
2665
- index.params_json = R"({ "store": { "indexType": "upside_down", "kvStoreName": "moss" }})";
3365
+ index.params_json = R"({ "store": { "kvStoreName": "moss" }})";
2666
3366
  couchbase::core::operations::management::search_index_upsert_request req{};
2667
3367
  req.index = index;
2668
3368
  auto resp = test::utils::execute(integration.cluster, req);