|
8 | 8 | #include "../src/foundation/constants.h" |
9 | 9 | #include "../src/foundation/log.h" |
10 | 10 | #include "test_framework.h" |
| 11 | +#include "test_helpers.h" |
11 | 12 | #include <cli/cli.h> |
12 | 13 | #include <mcp/mcp.h> |
13 | 14 | #include <pipeline/pipeline.h> |
14 | 15 | #include <store/store.h> |
| 16 | +#include <watcher/watcher.h> |
15 | 17 | #include <yyjson/yyjson.h> |
16 | 18 | #include <string.h> |
17 | 19 | #include <stdlib.h> |
@@ -4032,6 +4034,124 @@ TEST(readonly_query_succeeds_on_readonly_fs) { |
4032 | 4034 |
|
4033 | 4035 | #undef ROQ_PROJECT |
4034 | 4036 |
|
| 4037 | +/* ══════════════════════════════════════════════════════════════════ |
| 4038 | + * AUTO_WATCH GATE (distilled from PR #625) |
| 4039 | + * |
| 4040 | + * Background watcher registration on session connect is gated by the |
| 4041 | + * `auto_watch` config key (default TRUE = existing behavior). |
| 4042 | + * ══════════════════════════════════════════════════════════════════ */ |
| 4043 | + |
| 4044 | +/* Drive the already-indexed connect path (initialize → maybe_auto_index → |
| 4045 | + * watcher registration) and return the resulting watch count. |
| 4046 | + * auto_watch_value: NULL leaves the key unset (exercises the default), |
| 4047 | + * otherwise the key is set to that value before initialize. |
| 4048 | + * Returns a negative code on fixture setup failure. */ |
| 4049 | +static int auto_watch_connect_watch_count(const char *auto_watch_value) { |
| 4050 | + char cache[256]; |
| 4051 | + snprintf(cache, sizeof(cache), "/tmp/cbm-autowatch-cache-XXXXXX"); |
| 4052 | + if (!cbm_mkdtemp(cache)) { |
| 4053 | + return -1; |
| 4054 | + } |
| 4055 | + |
| 4056 | + char repodir[512]; |
| 4057 | + snprintf(repodir, sizeof(repodir), "%s/repo", cache); |
| 4058 | + if (th_mkdir_p(repodir) != 0) { |
| 4059 | + th_rmtree(cache); |
| 4060 | + return -2; |
| 4061 | + } |
| 4062 | + |
| 4063 | + /* Same derivation detect_session uses on the cwd — realpath-based, so |
| 4064 | + * the name matches even where /tmp is a symlink (macOS). */ |
| 4065 | + char *project = cbm_project_name_from_path(repodir); |
| 4066 | + if (!project) { |
| 4067 | + th_rmtree(cache); |
| 4068 | + return -3; |
| 4069 | + } |
| 4070 | + |
| 4071 | + /* Pre-create <cache>/<project>.db so maybe_auto_index takes the |
| 4072 | + * "already indexed" branch — the watcher-registration site under test. */ |
| 4073 | + char db_path[1024]; |
| 4074 | + snprintf(db_path, sizeof(db_path), "%s/%s.db", cache, project); |
| 4075 | + if (th_write_file(db_path, "") != 0) { |
| 4076 | + free(project); |
| 4077 | + th_rmtree(cache); |
| 4078 | + return -4; |
| 4079 | + } |
| 4080 | + free(project); |
| 4081 | + |
| 4082 | + const char *saved = getenv("CBM_CACHE_DIR"); |
| 4083 | + char *saved_copy = saved ? strdup(saved) : NULL; |
| 4084 | + cbm_setenv("CBM_CACHE_DIR", cache, 1); |
| 4085 | + |
| 4086 | + char old_cwd[1024]; |
| 4087 | + if (!cbm_getcwd(old_cwd, sizeof(old_cwd)) || cbm_chdir(repodir) != 0) { |
| 4088 | + restore_cache_dir(saved_copy); |
| 4089 | + free(saved_copy); |
| 4090 | + th_rmtree(cache); |
| 4091 | + return -5; |
| 4092 | + } |
| 4093 | + |
| 4094 | + int count = -6; |
| 4095 | + cbm_config_t *cfg = cbm_config_open(cache); |
| 4096 | + cbm_store_t *wstore = cbm_store_open_memory(); |
| 4097 | + cbm_watcher_t *watcher = wstore ? cbm_watcher_new(wstore, NULL, NULL) : NULL; |
| 4098 | + if (cfg && watcher) { |
| 4099 | + if (auto_watch_value) { |
| 4100 | + cbm_config_set(cfg, CBM_CONFIG_AUTO_WATCH, auto_watch_value); |
| 4101 | + } |
| 4102 | + |
| 4103 | + cbm_mcp_server_t *srv = cbm_mcp_server_new(NULL); |
| 4104 | + if (srv) { |
| 4105 | + cbm_mcp_server_set_watcher(srv, watcher); |
| 4106 | + cbm_mcp_server_set_config(srv, cfg); |
| 4107 | + char *resp = cbm_mcp_server_handle( |
| 4108 | + srv, "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{}}"); |
| 4109 | + free(resp); |
| 4110 | + count = cbm_watcher_watch_count(watcher); |
| 4111 | + cbm_mcp_server_free(srv); |
| 4112 | + } |
| 4113 | + } |
| 4114 | + |
| 4115 | + if (watcher) { |
| 4116 | + cbm_watcher_free(watcher); |
| 4117 | + } |
| 4118 | + if (wstore) { |
| 4119 | + cbm_store_close(wstore); |
| 4120 | + } |
| 4121 | + if (cfg) { |
| 4122 | + cbm_config_close(cfg); |
| 4123 | + } |
| 4124 | + |
| 4125 | + (void)cbm_chdir(old_cwd); |
| 4126 | + restore_cache_dir(saved_copy); |
| 4127 | + free(saved_copy); |
| 4128 | + th_rmtree(cache); |
| 4129 | + return count; |
| 4130 | +} |
| 4131 | + |
| 4132 | +/* Default (key unset) → watcher registered on connect. Guards the |
| 4133 | + * no-behavior-change promise of the auto_watch gate: existing users keep |
| 4134 | + * background auto-sync without touching config. */ |
| 4135 | +TEST(mcp_auto_watch_default_registers_watcher_on_connect) { |
| 4136 | + int count = auto_watch_connect_watch_count(NULL); |
| 4137 | + if (count < 0) { |
| 4138 | + PASS(); /* fixture setup failed (tmpdir/cwd unavailable) — skip */ |
| 4139 | + } |
| 4140 | + ASSERT_EQ(count, 1); |
| 4141 | + PASS(); |
| 4142 | +} |
| 4143 | + |
| 4144 | +/* auto_watch=false → NO watcher registered on connect. RED on pre-gate code |
| 4145 | + * (registration was unconditional and the key did not exist). */ |
| 4146 | +TEST(mcp_auto_watch_false_skips_watcher_on_connect) { |
| 4147 | + int count = auto_watch_connect_watch_count("false"); |
| 4148 | + if (count < 0) { |
| 4149 | + PASS(); /* fixture setup failed (tmpdir/cwd unavailable) — skip */ |
| 4150 | + } |
| 4151 | + ASSERT_EQ(count, 0); |
| 4152 | + PASS(); |
| 4153 | +} |
| 4154 | + |
4035 | 4155 | /* ══════════════════════════════════════════════════════════════════ |
4036 | 4156 | * SUITE |
4037 | 4157 | * ══════════════════════════════════════════════════════════════════ */ |
@@ -4206,4 +4326,8 @@ SUITE(mcp) { |
4206 | 4326 | RUN_TEST(tool_bad_project_name_no_overflow_issue235); |
4207 | 4327 | RUN_TEST(tool_bad_project_error_valid_json_issue235); |
4208 | 4328 | RUN_TEST(tool_resolve_store_by_internal_name_issue704); |
| 4329 | + |
| 4330 | + /* auto_watch gate (distilled from PR #625) */ |
| 4331 | + RUN_TEST(mcp_auto_watch_default_registers_watcher_on_connect); |
| 4332 | + RUN_TEST(mcp_auto_watch_false_skips_watcher_on_connect); |
4209 | 4333 | } |
0 commit comments