Ariver
2026-07-02 01bab5422712299b49f656e6b4c42a3bf314f519
src/Scripts/pro_custom_container_quota_qa.sh
@@ -26,6 +26,25 @@
        fail(message)
def function_body(source: str, signature: str) -> str:
    start = source.find(signature)
    if start == -1:
        fail(f"missing function signature: {signature}")
    brace = source.find("{", start)
    if brace == -1:
        fail(f"missing function body for: {signature}")
    depth = 0
    for index in range(brace, len(source)):
        char = source[index]
        if char == "{":
            depth += 1
        elif char == "}":
            depth -= 1
            if depth == 0:
                return source[brace + 1:index]
    fail(f"unterminated function body for: {signature}")
pro = read("ProEntitlement.swift")
data_layer = read("DataLayer.swift")
tag_editor = read("TagEditorView.swift")
@@ -57,7 +76,9 @@
require(data_layer, "static func moveApp(path: String, from sourceTag: String, to targetTag: String, color: Int, copy: Bool) -> TagEditorMutationResult", "moveApp must return a mutation result")
require(data_layer, "TagDatabase.markCustomMaintainedIfNeeded", "membership changes must mark maintained system tags")
require(data_layer, "static func reorderTags(_ names: [String]) -> Bool", "tag order persistence must return whether it saved")
require(data_layer, "guard ProEntitlementPolicy.isUnlocked(.persistentAppSorting) else { return false }", "tag order persistence must be Pro-gated")
reorder_tags_body = function_body(data_layer, "static func reorderTags(_ names: [String]) -> Bool")
if "ProEntitlementPolicy.isUnlocked(.persistentAppSorting)" in reorder_tags_body:
    fail("tag list order persistence must be available to free users")
require(tag_editor, "onCustomContainerQuotaExceeded", "TagEditorView must expose quota-blocked callback")
require(tag_editor, "onCustomContainerQuotaStatusChanged", "TagEditorView must expose quota status refresh callback")
@@ -66,8 +87,10 @@
require(tag_editor, "guard handleMutationResult(result) else { return }", "TagEditorView must not update local state before a save succeeds")
require(tag_editor, "blockedCustomContainerQuota", "TagEditorView must surface quota blocks")
require(content, "persistTagOrderIfAllowed()", "ContentView must route tag order save through a Pro gate")
require(content, "pro.tagSorting.previewToast", "free tag sorting preview toast is missing")
require(content, "persistTagOrder()", "ContentView must persist tag list order")
persist_tag_order_body = function_body(content, "private func persistTagOrder()")
if "proEntitlement.isUnlocked" in persist_tag_order_body or "pro.tagSorting.previewToast" in persist_tag_order_body:
    fail("free users must be able to persist tag list order without a Pro preview toast")
require(content, "presentCustomContainerQuotaPrompt", "ContentView must show custom container quota prompt")
require(content, "customContainerQuotaAccessory", "ContentView tag editor must show quota count")
require(content, "@State private var customContainerQuotaStatus = ProEntitlementPolicy.customContainerQuotaStatus()", "ContentView quota count must be backed by SwiftUI state")
@@ -84,19 +107,19 @@
require(preferences, "onCustomContainerQuotaStatusChanged: refreshSettingsCustomContainerQuotaStatus", "Settings must refresh quota count after TagEditorView saves")
require(preferences, "settingsCustomContainerQuotaOverviewStatus = quotaStatus", "Settings scanApps must initialize quota count from the same store")
require(preferences, 'title: tr("pro.feature.maintainableTagCount")', "Pro comparison must include maintainable tag count row")
require(preferences, 'title: tr("pro.feature.tagOrderPersistence")', "Pro comparison must include tag order row")
if 'title: tr("pro.feature.tagOrderPersistence")' in preferences:
    fail("Pro comparison must not list tag list order; it is available to free users")
require(preferences, 'title: tr("pro.feature.containerAppOrderPersistence")', "Pro comparison must include container app order row")
first_three = [
first_two = [
    preferences.find('title: tr("pro.feature.maintainableTagCount")'),
    preferences.find('title: tr("pro.feature.tagOrderPersistence")'),
    preferences.find('title: tr("pro.feature.containerAppOrderPersistence")'),
]
custom_color_index = preferences.find('title: tr("pro.feature.customTagColors")')
if any(index == -1 for index in first_three) or custom_color_index == -1:
if any(index == -1 for index in first_two) or custom_color_index == -1:
    fail("could not locate Pro comparison feature rows")
if not (first_three[0] < first_three[1] < first_three[2] < custom_color_index):
    fail("new Pro comparison rows must be the first three rows")
if not (first_two[0] < first_two[1] < custom_color_index):
    fail("Pro comparison rows must begin with tag quota and container app order")
required_l10n = [
    "pro.feature.customContainerQuota",
@@ -106,9 +129,7 @@
    "pro.customContainers.quotaUnlimitedShort",
    "pro.customContainers.quotaPrompt",
    "pro.customContainers.manageExisting",
    "pro.tagSorting.previewToast",
    "pro.feature.maintainableTagCount",
    "pro.feature.tagOrderPersistence",
    "pro.feature.containerAppOrderPersistence",
    "settings.proStatus.maxSixTags",
    "settings.proStatus.unlimitedTags",
@@ -139,5 +160,5 @@
if zh_hans.get("settings.proStatus.unlimitedTags") != "✅没有限制":
    fail("zh-Hans unlimited tag status must include the approved leading checkmark")
print("PASS Pro custom container quota QA: quota gate, tag-order preview, Pro table rows, and 29-language keys are wired")
print("PASS Pro custom container quota QA: quota gate, free tag-order persistence, Pro table rows, and 29-language keys are wired")
PY