Skip to content

Commit 0e75783

Browse files
barkha06sanjivanipatraxvipbhardwajborrrdenpasin
authored
Merge CBL 3.3.0 Tests (#270)
* Added conflicts, end-to-end,system and volume tests for CBL -3.3.0. * Added mesh sanity, consistency, peer addition and removal and network partition tests * Added test for Large-Size Doc replication --------- Co-authored-by: Pasin Suriyentrakorn <pasin@couchbase.com> CBL-7192: Don't reject a payload because of unknown keys (#241) This means that any addition in the spec will be invalid until handled properly, which causes dev friction Fix errors present in Jenkinsfile (#242) * Put all locks in a single lock statement * Make CBL_BUILD optional to prepare for its removal Fix flaw in Android merge-dict conflict resolver It was setting "docProp" on the resulting dictionary instead of "key" Update verify_python.sh Add explicit package bases to tests folder for validation Use full path in import to account for new setting * Add merge-dict conflict resolver spec and Implement in iOS (#234) * Added merge-dict conflict resolver spec. * Bump API spec version to 2.0.1. * Implemented merge-dict resolver in iOS. Update iOS TestServer to log to console synchronously * Removed changes to multipeer_funtional/topology.json * Set download param in all multipeer topology files --------- Co-authored-by: Sanjivani Patra <sanjivani.patra@couchbase.com> Co-authored-by: vipbhardwaj <vipulbhardwaj1011@gmail.com> Co-authored-by: Jim Borden <jim.borden@couchbase.com> Co-authored-by: Pasin Suriyentrakorn <pasin@couchbase.com>
1 parent 0bcdbf5 commit 0e75783

25 files changed

Lines changed: 2311 additions & 281 deletions

File tree

.github/workflows/verify_python.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ uv pip install "./client[dev]"
1818
echo "Checking client files (including smoke tests)..."
1919
${VENV_DIR}/bin/python -m mypy client
2020
echo "Checking tests files..."
21-
python -m mypy tests
21+
python -m mypy tests --explicit-package-bases
2222
cleanup_venv
2323

2424
create_venv

client/src/cbltest/api/couchbaseserver.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,41 @@ def run_query(
257257

258258
return list(dict(result) for result in query_obj.execute())
259259

260+
def upsert_document(
261+
self,
262+
bucket: str,
263+
doc_id: str,
264+
document: dict,
265+
scope: str = "_default",
266+
collection: str = "_default",
267+
) -> None:
268+
"""
269+
Inserts a document into the specified bucket.scope.collection.
270+
271+
:param bucket: The bucket name.
272+
:param scope: The scope name.
273+
:param collection: The collection name.
274+
:param doc_id: The document ID.
275+
:param document: The document content (a dictionary).
276+
"""
277+
with self.__tracer.start_as_current_span(
278+
"insert_document",
279+
attributes={
280+
"cbl.bucket.name": bucket,
281+
"cbl.scope.name": scope,
282+
"cbl.collection.name": collection,
283+
"cbl.document.id": doc_id,
284+
},
285+
):
286+
try:
287+
bucket_obj = _try_n_times(10, 1, False, self.__cluster.bucket, bucket)
288+
coll = bucket_obj.scope(scope).collection(collection)
289+
coll.upsert(doc_id, document)
290+
except Exception as e:
291+
raise CblTestError(
292+
f"Failed to insert document '{doc_id}' into {bucket}.{scope}.{collection}: {e}"
293+
)
294+
260295
def start_xdcr(self, target: "CouchbaseServer", bucket_name: str) -> None:
261296
"""
262297
Starts an XDCR replication from this cluster to the target cluster
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import random
2+
import sys
3+
import time
4+
import uuid
5+
from concurrent.futures import ThreadPoolExecutor
6+
from typing import Any, Callable, Dict, List, Optional
7+
8+
9+
class JSONGenerator:
10+
"""
11+
Utility class to generate and update reproducible JSON documents for testing.
12+
13+
Usage:
14+
gen = JSONGenerator(size=1000, format="json")
15+
docs = gen.generate_all_documents()
16+
updated_docs = gen.update_all_documents(docs)
17+
18+
Parameters:
19+
seed (int, optional): Random seed for reproducibility (default: random int).
20+
size (int, optional): Number of documents to generate (default: 60000).
21+
format (str, optional): Output format - "json" (dict) or "key-value" (list of dicts/documents).
22+
To insert/update in CB-server/SGW/ Edge-server : use format "json".
23+
To insert into test-server use format "key-value" .
24+
"""
25+
26+
def __init__(
27+
self,
28+
seed: int = random.randint(0, sys.maxsize),
29+
size: int = 60000,
30+
format: str = "json",
31+
):
32+
self.seed = seed
33+
self.size = size
34+
self.format = format
35+
36+
def generate_document(self, doc_id: str) -> Dict[str, Any]:
37+
"""Generate a single JSON document with reproducible random data"""
38+
random.seed(self.seed + int(doc_id.split("-")[0], 16))
39+
if self.format == "json":
40+
return {
41+
doc_id: {
42+
"data": {
43+
"temperature": random.uniform(-20, 40),
44+
"humidity": random.randint(0, 100),
45+
"status": random.choice(["active", "inactive", "maintenance"]),
46+
},
47+
"metadata": {
48+
"version": 1,
49+
"created_at": int(time.time()),
50+
"modified_at": int(time.time()),
51+
},
52+
}
53+
}
54+
else:
55+
return {
56+
doc_id: [
57+
{
58+
"data": {
59+
"temperature": random.uniform(-20, 40),
60+
"humidity": random.randint(0, 100),
61+
"status": random.choice(
62+
["active", "inactive", "maintenance"]
63+
),
64+
}
65+
},
66+
{
67+
"metadata": {
68+
"version": 1,
69+
"created_at": int(time.time()),
70+
"modified_at": int(time.time()),
71+
}
72+
},
73+
]
74+
}
75+
76+
def update_document(self, doc: Any, doc_id: str) -> Dict[str, Any]:
77+
"""Update a document with reproducible modifications"""
78+
offset = int(doc_id.split("-")[0], 16)
79+
random.seed(self.seed + offset)
80+
if self.format == "json":
81+
doc["data"]["temperature"] += random.uniform(-5, 5)
82+
doc["data"]["humidity"] = (
83+
doc["data"]["humidity"] + random.randint(-10, 10)
84+
) % 100
85+
doc["data"]["status"] = random.choice(["active", "inactive", "maintenance"])
86+
doc["metadata"]["version"] = doc["metadata"]["version"] + 1
87+
doc["metadata"]["modified_at"] = int(time.time())
88+
89+
else:
90+
doc[0]["data"] = {
91+
"temperature": random.uniform(-20, 40),
92+
"humidity": random.randint(0, 100),
93+
"status": random.choice(["active", "inactive", "maintenance"]),
94+
}
95+
doc[1]["metadata"]["version"] = doc[1]["metadata"]["version"] + 1
96+
doc[1]["metadata"]["modified_at"] = int(time.time())
97+
return {doc_id: doc}
98+
99+
def batch_process(
100+
self,
101+
process_fn: Callable,
102+
items_ids: List[Any],
103+
items_doc: Optional[Dict[str, Any]] = None,
104+
batch_size: int = 1000,
105+
) -> Dict[Any, Any]:
106+
"""Generic batch processing function with threading"""
107+
results = {}
108+
109+
def process_batch(batch):
110+
result = {}
111+
for item in batch:
112+
output = (
113+
process_fn(items_doc[item], item)
114+
if items_doc is not None
115+
else process_fn(item)
116+
)
117+
result.update(output)
118+
return result
119+
120+
with ThreadPoolExecutor() as executor:
121+
futures = [
122+
executor.submit(process_batch, items_ids[i : i + batch_size])
123+
for i in range(0, len(items_ids), batch_size)
124+
]
125+
for future in futures:
126+
results.update(future.result())
127+
128+
return results
129+
130+
def generate_all_documents(self, size=None) -> Dict[str, Any]:
131+
"""Generate all documents using parallel processing"""
132+
if size is None:
133+
size = self.size
134+
135+
doc_ids = [str(uuid.uuid4()) for _ in range(size)]
136+
documents = self.batch_process(self.generate_document, doc_ids)
137+
return documents
138+
139+
def update_all_documents(
140+
self, documents: Dict[str, Any]
141+
) -> Dict[str, Dict[str, Any]]:
142+
"""Update all documents with consistent modifications"""
143+
144+
doc_ids = list(documents.keys())
145+
updated = self.batch_process(self.update_document, doc_ids, documents)
146+
return updated

jenkins/pipelines/QE/multiplatform/Jenkinsfile

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ pipeline {
33
parameters {
44
string(
55
name: 'PLATFORM_VERSIONS',
6-
defaultValue: 'ios:3.2.3 android:3.2.4',
6+
defaultValue: 'ios:3.3.0 android:3.3.0',
77
description: 'Platform versions in two supported formats:\n' +
88
'1. Auto-fetch (recommended): platform1:version1 platform2:version2\n' +
99
' Example: "ios:3.2.3 android:3.2.4"\n' +
@@ -23,11 +23,22 @@ pipeline {
2323
defaultValue: 'test_no_conflicts::TestNoConflicts::test_multiple_cbls_updates_concurrently_with_pull',
2424
description: 'Name of the test to run, leave empty to run all tests, or just mention a directory name[::class name] to run tests in that directory[::class]'
2525
)
26+
string(
27+
name: 'TOPOLOGY_FILE',
28+
defaultValue: 'topology.json',
29+
description: 'Multiplatform Topology file in JSON format'
30+
)
2631
booleanParam(
2732
name: 'DISABLE_AUTO_FETCH',
2833
defaultValue: false,
2934
description: 'Disable automatic fetching of latest successful builds (requires explicit build numbers in PLATFORM_VERSIONS)'
3035
)
36+
booleanParam(
37+
name: 'DISABLE_PREBUILD',
38+
defaultValue: false,
39+
description: 'Disable automatic prebuilding of testserver)'
40+
)
41+
3142
}
3243
stages {
3344
stage('Init') {
@@ -111,17 +122,19 @@ pipeline {
111122
parallelBuilds[platform] = {
112123
// For multiplatform, we'll use a generic version since each platform may have different versions
113124
// The actual version assignment happens during test setup
125+
114126
build job: 'prebuild-test-server',
115127
parameters: [
116128
string(name: 'TS_PLATFORM', value: platform),
117-
string(name: 'CBL_VERSION', value: '3.2.3'), // Generic version for prebuild
129+
string(name: 'CBL_VERSION', value: '3.3.0'), // Generic version for prebuild
130+
string(name: 'CBL_BUILD', value: ''),
118131
],
119132
wait: true,
120133
propagate: true
121134
}
122135
}
123136

124-
if (parallelBuilds.size() > 0) {
137+
if (parallelBuilds.size() > 0 && !params.DISABLE_PREBUILD) {
125138
parallel parallelBuilds
126139
} else {
127140
echo "No test servers to prebuild"
@@ -130,25 +143,31 @@ pipeline {
130143
}
131144
}
132145
stage('Setup and Run Tests') {
133-
agent { label 'mac-mini-new' }
146+
agent { label 'mob-e2e-mac-01' }
134147
environment {
135-
KEYCHAIN_PASSWORD = credentials('mobile-qe-keychain')
136-
PATH = "/opt/homebrew/opt/python@3.10/bin:/opt/homebrew/bin:/usr/local/bin:${env.PATH}"
137-
AWS_PROFILE = "mobile-for-now"
148+
KEYCHAIN_PASSWORD = credentials('mob-e2e-mac-01-keychain-password')
149+
DOTNET_ROOT = "/opt/homebrew/opt/dotnet/libexec"
150+
PATH = "/Users/qe_mobile_india/.local/bin:/Library/Java/JavaVirtualMachines/temurin-11.jdk/Contents/Home/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/Library/Apple/usr/bin:/Users/qe_mobile_india/.dotnet/tools:/opt/homebrew/opt/dotnet:${DOTNET_ROOT}:/Users/qe_mobile_india/Library/Android/sdk/cmdline-tools/latest/bin:/Users/qe_mobile_india/Library/Android/sdk/platform-tools:/Users/qe_mobile_india/Library/Android/sdk/emulator:${PATH}"
151+
AWS_PROFILE = "default"
152+
ANDROID_HOME = "/Users/qe_mobile_india/Library/Android/sdk"
153+
138154
}
139155
steps {
140156
// Unlock keychain:
141157
sh 'security unlock-keychain -p ${KEYCHAIN_PASSWORD} ~/Library/Keychains/login.keychain-db'
142158
echo "Run Multiplatform Test"
143-
timeout(time: 60, unit: 'MINUTES') {
144-
sh "jenkins/pipelines/QE/multiplatform/test_multiplatform.sh ${params.PLATFORM_VERSIONS} ${params.SGW_VERSION} ${params.CBL_TEST_NAME}"
159+
timeout(time: 25, unit: 'HOURS') {
160+
sh "jenkins/pipelines/QE/multiplatform/test_multiplatform.sh ${params.PLATFORM_VERSIONS} ${params.SGW_VERSION} ${params.CBL_TEST_NAME} ${params.TOPOLOGY_FILE}"
145161
}
146162
}
163+
post {
164+
always {
165+
timeout(time: 5, unit: 'MINUTES') {
166+
sh 'jenkins/pipelines/QE/multiplatform/teardown.sh'
167+
}
168+
archiveArtifacts artifacts: 'tests/QE/session.log', fingerprint: true, allowEmptyArchive: true
169+
archiveArtifacts artifacts: 'tests/QE/http_log/*', fingerprint: true, allowEmptyArchive: true
170+
}
147171
}
148172
}
149-
post {
150-
failure {
151-
mail bcc: '', body: "Project: <a href='${env.BUILD_URL}'>${env.JOB_NAME}</a> has failed!", cc: '', charset: 'UTF-8', from: 'jenkins@couchbase.com', mimeType: 'text/html', replyTo: 'no-reply@couchbase.com', subject: "${env.JOB_NAME} failed", to: "vipul.bhardwaj@couchbase.com";
152-
}
153-
}
154-
}
173+
}
Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
11
{
22
"$schema": "https://packages.couchbase.com/couchbase-lite/testserver.schema.json",
3-
"test-servers": [],
4-
"sync-gateways": [{"hostname": "{{test-client-ip}}", "tls": true}],
5-
"couchbase-servers": [{"hostname": "{{test-client-ip}}"}],
6-
"logslurp": "{{test-client-ip}}:8180",
7-
"greenboard": {"hostname": "jenkins.mobiledev.couchbase.com", "username": "writer", "password": "couchbase2" },
83
"api-version": 1
94
}

0 commit comments

Comments
 (0)