chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* repro_issue571.c — Reproduce-first case for OPEN bug #571.
|
||||
*
|
||||
* BUG: "Project name strips non-ASCII (CJK) characters from path,
|
||||
* resulting in truncated/unrecognizable names"
|
||||
* https://github.com/DeusData/codebase-memory-mcp/issues/571
|
||||
*
|
||||
* ROOT CAUSE (src/pipeline/fqn.c, cbm_project_name_from_path, lines ~341-348):
|
||||
*
|
||||
* The function maps every byte that is not in [A-Za-z0-9._-] to '-':
|
||||
*
|
||||
* unsigned char c = (unsigned char)path[i];
|
||||
* bool safe = (c >= 'a' && c <= 'z') || ... || c == '-';
|
||||
* if (!safe) path[i] = '-';
|
||||
*
|
||||
* UTF-8 encodes each CJK code-point as 3 consecutive bytes, all with
|
||||
* values >= 0x80 (> 127). Every one of those bytes fails the safe-char
|
||||
* test and is rewritten to '-'. The subsequent dash-collapse pass then
|
||||
* folds the run of dashes from a CJK segment into a single '-', and the
|
||||
* leading/trailing trim can erase it entirely if it was the final segment.
|
||||
*
|
||||
* For the exact path from the issue report:
|
||||
* Input: "/Users/yunxin/Desktop/开发/后端/信租风控通后端"
|
||||
* Buggy: "Users-yunxin-Desktop" (all three CJK segments stripped)
|
||||
* Correct: result MUST contain something beyond "Users-yunxin-Desktop"
|
||||
* and MUST NOT be empty. Whether the fix preserves the raw
|
||||
* UTF-8 bytes ("开发"), percent-encodes them ("%E5%BC%80%E5%8F%91"),
|
||||
* or uses another scheme is left to the implementer — this test
|
||||
* pins the invariants:
|
||||
* (a) non-NULL and non-empty result
|
||||
* (b) for a path whose last segment is purely CJK, the output
|
||||
* is LONGER than the result produced from the ASCII-only
|
||||
* prefix of that same path (proving the CJK segment
|
||||
* contributes something rather than collapsing to nothing)
|
||||
* (c) the result is NOT equal to the ASCII-prefix-only slug
|
||||
* "Users-yunxin-Desktop" that the buggy code returns
|
||||
*
|
||||
* EXPECTED vs ACTUAL:
|
||||
* Input path : /Users/yunxin/Desktop/开发/后端/信租风控通后端
|
||||
* Expected : non-empty slug that encodes the CJK components somehow
|
||||
* Actual : "Users-yunxin-Desktop" (CJK segments silently dropped)
|
||||
*
|
||||
* The PRIMARY assertion — ASSERT_STR_NEQ(name, ascii_only_slug) — is RED
|
||||
* on unpatched code because the buggy function returns exactly
|
||||
* "Users-yunxin-Desktop", which IS the ascii_only_slug.
|
||||
*
|
||||
* DECLARATION:
|
||||
* char *cbm_project_name_from_path(const char *abs_path);
|
||||
* declared in <pipeline/pipeline.h>
|
||||
*/
|
||||
|
||||
#include "test_framework.h"
|
||||
#include <pipeline/pipeline.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* ── Test ─────────────────────────────────────────────────────────── */
|
||||
|
||||
/*
|
||||
* Single test with three layered assertions (all RED on unpatched code):
|
||||
*
|
||||
* 1. Result is non-NULL and non-empty (the fallback "root" would be wrong
|
||||
* too, but the primary bug is the silent CJK strip).
|
||||
* 2. Result is NOT equal to the ASCII-prefix-only slug. On buggy code the
|
||||
* function returns exactly that slug, so this fires.
|
||||
* 3. Result is strictly longer than the ASCII-prefix slug. Any scheme that
|
||||
* preserves CJK (raw UTF-8, percent-encoding, or even a hex dump) must
|
||||
* produce a longer string than the stripped version.
|
||||
*/
|
||||
TEST(repro_issue571_cjk_project_name) {
|
||||
/*
|
||||
* Exact path from the issue report. The last three path segments
|
||||
* (开发, 后端, 信租风控通后端) are all CJK-only; none contains any
|
||||
* ASCII byte. The ASCII-only prefix ends at "Desktop".
|
||||
*/
|
||||
static const char *cjk_path =
|
||||
"/Users/yunxin/Desktop/\xe5\xbc\x80\xe5\x8f\x91"
|
||||
"/\xe5\x90\x8e\xe7\xab\xaf"
|
||||
"/\xe4\xbf\xa1\xe7\xa7\x9f\xe9\xa3\x8e\xe6\x8e\xa7\xe9\x80\x9a\xe5\x90\x8e\xe7\xab\xaf";
|
||||
/*
|
||||
* UTF-8 bytes spelled out above:
|
||||
* 开发 = U+5F00 U+53D1 = \xe5\xbc\x80 \xe5\x8f\x91
|
||||
* 后端 = U+540E U+7AEF = \xe5\x90\x8e \xe7\xab\xaf
|
||||
* 信租风控通后端 = U+4FE1 U+79DF U+98CE U+63A7 U+901A U+540E U+7AEF
|
||||
* = \xe4\xbf\xa1 \xe7\xa7\x9f \xe9\xa3\x8e
|
||||
* \xe6\x8e\xa7 \xe9\x80\x9a \xe5\x90\x8e \xe7\xab\xaf
|
||||
*
|
||||
* The ASCII-only prefix slug produced by the BUGGY implementation:
|
||||
* "Users-yunxin-Desktop"
|
||||
* This string is used in assertions 2 and 3 to prove the CJK segments
|
||||
* were silently erased.
|
||||
*/
|
||||
static const char *ascii_only_slug = "Users-yunxin-Desktop";
|
||||
|
||||
char *name = cbm_project_name_from_path(cjk_path);
|
||||
|
||||
/* ── Assertion 1: result must exist and be non-empty ─────────── */
|
||||
/* Even on buggy code this passes (the function returns the ASCII
|
||||
* prefix rather than NULL or "root"), so it serves only as a
|
||||
* pre-condition that the function did not crash or return NULL. */
|
||||
ASSERT_NOT_NULL(name);
|
||||
ASSERT_TRUE(strlen(name) > 0);
|
||||
|
||||
/* ── Assertion 2 (PRIMARY RED): CJK segments must not vanish ─── */
|
||||
/* On buggy code name == "Users-yunxin-Desktop" == ascii_only_slug.
|
||||
* After a correct fix name will encode the CJK components somehow
|
||||
* and therefore differ from the stripped ASCII slug. */
|
||||
ASSERT_STR_NEQ(name, ascii_only_slug);
|
||||
|
||||
/* ── Assertion 3 (SECONDARY RED): CJK contribution lengthens result */
|
||||
/* Any faithful encoding of the CJK bytes (raw UTF-8, percent-encode,
|
||||
* hex) is longer than the ASCII-only slug. On buggy code
|
||||
* strlen(name) == strlen(ascii_only_slug) == 20, so this also FAILS. */
|
||||
ASSERT_TRUE(strlen(name) > strlen(ascii_only_slug));
|
||||
|
||||
free(name);
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* ── Suite ────────────────────────────────────────────────────────── */
|
||||
SUITE(repro_issue571) {
|
||||
RUN_TEST(repro_issue571_cjk_project_name);
|
||||
}
|
||||
Reference in New Issue
Block a user