blob: 327d9c594b1fdaaf4dcc95e72d6de90e3cc833bd [file]
################################################################################
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
################################################################################
"""DTO and resource-path unit tests for Catalog branch CRUD wire format.
These tests pin the Python wire format to the Java contract so review
against ``paimon-api/.../rest/requests/CreateBranchRequest.java``,
``RenameBranchRequest.java``, ``ForwardBranchRequest.java``,
``responses/ListBranchesResponse.java`` and ``ResourcePaths.java`` does
not need to inspect a running mock server. They explicitly assert the
*exact* JSON field names and URL templates Java uses, and that
``ListBranchesResponse`` is **not** paged (Java's ``listBranches``
returns plain ``List<String>`` — pypaimon mirrors this).
"""
import json
import unittest
from pypaimon.api.api_request import (CreateBranchRequest, ForwardBranchRequest,
RenameBranchRequest)
from pypaimon.api.api_response import ListBranchesResponse
from pypaimon.api.resource_paths import ResourcePaths
from pypaimon.common.json_util import JSON
class CreateBranchRequestSerdeTest(unittest.TestCase):
def test_with_tag_uses_java_field_names(self):
request = CreateBranchRequest(branch="b1", from_tag="t1")
parsed = json.loads(JSON.to_json(request))
# Field names are Java's ``branch`` / ``fromTag`` — not ``branch_name``.
self.assertEqual(parsed["branch"], "b1")
self.assertEqual(parsed["fromTag"], "t1")
self.assertEqual(set(parsed.keys()), {"branch", "fromTag"})
def test_without_tag_serializes_null_from_tag(self):
request = CreateBranchRequest(branch="b1")
parsed = json.loads(JSON.to_json(request))
self.assertEqual(parsed["branch"], "b1")
# ``fromTag`` is included as null when not set (matches Java
# @Nullable behavior; jackson serializes nullable Long as null).
self.assertIsNone(parsed.get("fromTag"))
class RenameBranchRequestSerdeTest(unittest.TestCase):
def test_uses_to_branch_field(self):
request = RenameBranchRequest(to_branch="b2")
parsed = json.loads(JSON.to_json(request))
self.assertEqual(parsed, {"toBranch": "b2"})
class ForwardBranchRequestSerdeTest(unittest.TestCase):
def test_serializes_empty_object(self):
# Java's ForwardBranchRequest is an empty body. The Python wire DTO
# must serialize to {} (not null, not an array, not absent).
rendered = JSON.to_json(ForwardBranchRequest())
self.assertEqual(json.loads(rendered), {})
class ListBranchesResponseSerdeTest(unittest.TestCase):
def test_from_json_populates_branches(self):
response = JSON.from_json(
json.dumps({"branches": ["b1", "b2"]}),
ListBranchesResponse,
)
self.assertEqual(response.branches, ["b1", "b2"])
def test_response_is_not_paged(self):
# ListBranchesResponse must NOT be a paged response — Java's
# ListBranchesResponse has no ``nextPageToken``. This locks down
# the contract; if a future change accidentally extends it to
# PagedResponse this test fails.
response = ListBranchesResponse(branches=["b1"])
self.assertFalse(hasattr(response, "next_page_token"))
# Serialized form must not contain the paging key.
self.assertNotIn("nextPageToken", JSON.to_json(response))
class ResourcePathsBranchesTest(unittest.TestCase):
def setUp(self):
self.paths = ResourcePaths(prefix="mock")
def test_branches_collection_url(self):
self.assertEqual(
self.paths.branches("db", "tbl"),
"/v1/mock/databases/db/tables/tbl/branches",
)
def test_single_branch_url(self):
self.assertEqual(
self.paths.branch("db", "tbl", "b1"),
"/v1/mock/databases/db/tables/tbl/branches/b1",
)
def test_rename_branch_url(self):
self.assertEqual(
self.paths.rename_branch("db", "tbl", "b1"),
"/v1/mock/databases/db/tables/tbl/branches/b1/rename",
)
def test_forward_branch_url(self):
self.assertEqual(
self.paths.forward_branch("db", "tbl", "b1"),
"/v1/mock/databases/db/tables/tbl/branches/b1/forward",
)
def test_branch_url_url_encodes_branch_name(self):
# RESTUtil.encode_string escapes characters that are not URL-safe;
# a space round-trips to ``%20``. Mirrors the existing tag-name path.
self.assertEqual(
self.paths.branch("db", "tbl", "release 1.0"),
"/v1/mock/databases/db/tables/tbl/branches/release%201.0",
)
if __name__ == "__main__":
unittest.main()