Fossil SCM
Robustify a few things in the JSON subsystem integration and get JSON tests passing.
Commit
a588e55fe7feed2a8012edf362b9cb8b9dd475671b7c554d0fa0acb9a338eef2
Parent
8eec01d419508de…
10 files changed
+3
+6
+13
-2
+13
+17
-1
+1
+50
-21
+50
-21
+38
-20
+38
-20
+3
| --- src/cgi.c | ||
| +++ src/cgi.c | ||
| @@ -1745,10 +1745,13 @@ | ||
| 1745 | 1745 | char *z, *zToken; |
| 1746 | 1746 | const char *zType = 0; |
| 1747 | 1747 | int i, content_length = 0; |
| 1748 | 1748 | char zLine[2000]; /* A single line of input. */ |
| 1749 | 1749 | |
| 1750 | +#ifdef FOSSIL_ENABLE_JSON | |
| 1751 | + if( nCycles==0 ){ json_main_bootstrap(); } | |
| 1752 | +#endif | |
| 1750 | 1753 | if( zIpAddr ){ |
| 1751 | 1754 | if( nCycles==0 ){ |
| 1752 | 1755 | cgi_setenv("REMOTE_ADDR", zIpAddr); |
| 1753 | 1756 | g.zIpAddr = mprintf("%s", zIpAddr); |
| 1754 | 1757 | } |
| 1755 | 1758 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -1745,10 +1745,13 @@ | |
| 1745 | char *z, *zToken; |
| 1746 | const char *zType = 0; |
| 1747 | int i, content_length = 0; |
| 1748 | char zLine[2000]; /* A single line of input. */ |
| 1749 | |
| 1750 | if( zIpAddr ){ |
| 1751 | if( nCycles==0 ){ |
| 1752 | cgi_setenv("REMOTE_ADDR", zIpAddr); |
| 1753 | g.zIpAddr = mprintf("%s", zIpAddr); |
| 1754 | } |
| 1755 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -1745,10 +1745,13 @@ | |
| 1745 | char *z, *zToken; |
| 1746 | const char *zType = 0; |
| 1747 | int i, content_length = 0; |
| 1748 | char zLine[2000]; /* A single line of input. */ |
| 1749 | |
| 1750 | #ifdef FOSSIL_ENABLE_JSON |
| 1751 | if( nCycles==0 ){ json_main_bootstrap(); } |
| 1752 | #endif |
| 1753 | if( zIpAddr ){ |
| 1754 | if( nCycles==0 ){ |
| 1755 | cgi_setenv("REMOTE_ADDR", zIpAddr); |
| 1756 | g.zIpAddr = mprintf("%s", zIpAddr); |
| 1757 | } |
| 1758 |
M
src/db.c
+6
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -78,10 +78,16 @@ | ||
| 78 | 78 | va_start(ap, zFormat); |
| 79 | 79 | z = vmprintf(zFormat, ap); |
| 80 | 80 | va_end(ap); |
| 81 | 81 | #ifdef FOSSIL_ENABLE_JSON |
| 82 | 82 | if( g.json.isJsonMode ){ |
| 83 | + /* | |
| 84 | + ** Avoid calling into the JSON support subsystem if it | |
| 85 | + ** has not yet been initialized, e.g. early SQLite log | |
| 86 | + ** messages, etc. | |
| 87 | + */ | |
| 88 | + if( !json_is_main_boostrapped() ) json_main_bootstrap(); | |
| 83 | 89 | json_err( 0, z, 1 ); |
| 84 | 90 | } |
| 85 | 91 | else |
| 86 | 92 | #endif /* FOSSIL_ENABLE_JSON */ |
| 87 | 93 | if( g.xferPanic && g.cgiOutput==1 ){ |
| 88 | 94 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -78,10 +78,16 @@ | |
| 78 | va_start(ap, zFormat); |
| 79 | z = vmprintf(zFormat, ap); |
| 80 | va_end(ap); |
| 81 | #ifdef FOSSIL_ENABLE_JSON |
| 82 | if( g.json.isJsonMode ){ |
| 83 | json_err( 0, z, 1 ); |
| 84 | } |
| 85 | else |
| 86 | #endif /* FOSSIL_ENABLE_JSON */ |
| 87 | if( g.xferPanic && g.cgiOutput==1 ){ |
| 88 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -78,10 +78,16 @@ | |
| 78 | va_start(ap, zFormat); |
| 79 | z = vmprintf(zFormat, ap); |
| 80 | va_end(ap); |
| 81 | #ifdef FOSSIL_ENABLE_JSON |
| 82 | if( g.json.isJsonMode ){ |
| 83 | /* |
| 84 | ** Avoid calling into the JSON support subsystem if it |
| 85 | ** has not yet been initialized, e.g. early SQLite log |
| 86 | ** messages, etc. |
| 87 | */ |
| 88 | if( !json_is_main_boostrapped() ) json_main_bootstrap(); |
| 89 | json_err( 0, z, 1 ); |
| 90 | } |
| 91 | else |
| 92 | #endif /* FOSSIL_ENABLE_JSON */ |
| 93 | if( g.xferPanic && g.cgiOutput==1 ){ |
| 94 |
+13
-2
| --- src/json.c | ||
| +++ src/json.c | ||
| @@ -707,10 +707,21 @@ | ||
| 707 | 707 | cson_value * json_req_payload_get(char const *pKey){ |
| 708 | 708 | return g.json.reqPayload.o |
| 709 | 709 | ? cson_object_get(g.json.reqPayload.o,pKey) |
| 710 | 710 | : NULL; |
| 711 | 711 | } |
| 712 | + | |
| 713 | +/* | |
| 714 | +** Returns non-zero if the json_main_bootstrap() function has already | |
| 715 | +** been called. In general, this function should be used sparingly, | |
| 716 | +** e.g. from low-level support functions like fossil_warning() where | |
| 717 | +** there is genuine uncertainty about whether (or not) the JSON setup | |
| 718 | +** has already been called. | |
| 719 | +*/ | |
| 720 | +int json_is_main_boostrapped(){ | |
| 721 | + return ((g.json.gc.v != NULL) && (g.json.gc.a != NULL)); | |
| 722 | +} | |
| 712 | 723 | |
| 713 | 724 | /* |
| 714 | 725 | ** Initializes some JSON bits which need to be initialized relatively |
| 715 | 726 | ** early on. It should only be called from cgi_init() or |
| 716 | 727 | ** json_cmd_top() (early on in those functions). |
| @@ -931,11 +942,11 @@ | ||
| 931 | 942 | ** This must be called by the top-level JSON command dispatching code |
| 932 | 943 | ** before they do any work. |
| 933 | 944 | ** |
| 934 | 945 | ** This must only be called once, or an assertion may be triggered. |
| 935 | 946 | */ |
| 936 | -static void json_mode_bootstrap(){ | |
| 947 | +void json_mode_bootstrap(){ | |
| 937 | 948 | static char once = 0 /* guard against multiple runs */; |
| 938 | 949 | char const * zPath = P("PATH_INFO"); |
| 939 | 950 | assert(g.json.gc.a && "json_main_bootstrap() was not called!"); |
| 940 | 951 | assert( (0==once) && "json_mode_bootstrap() called too many times!"); |
| 941 | 952 | if( once ){ |
| @@ -2262,11 +2273,11 @@ | ||
| 2262 | 2273 | ** json_cmd_top(). |
| 2263 | 2274 | */ |
| 2264 | 2275 | void json_page_top(void){ |
| 2265 | 2276 | char const * zCommand; |
| 2266 | 2277 | assert(g.json.gc.a && "json_main_bootstrap() was not called!"); |
| 2267 | - json_mode_bootstrap(); | |
| 2278 | + assert(g.json.cmd.a && "json_mode_bootstrap() was not called!"); | |
| 2268 | 2279 | zCommand = json_command_arg(1); |
| 2269 | 2280 | if(!zCommand || !*zCommand){ |
| 2270 | 2281 | json_dispatch_missing_args_err( JsonPageDefs, |
| 2271 | 2282 | "No command (sub-path) specified." |
| 2272 | 2283 | " Try one of: "); |
| 2273 | 2284 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -707,10 +707,21 @@ | |
| 707 | cson_value * json_req_payload_get(char const *pKey){ |
| 708 | return g.json.reqPayload.o |
| 709 | ? cson_object_get(g.json.reqPayload.o,pKey) |
| 710 | : NULL; |
| 711 | } |
| 712 | |
| 713 | /* |
| 714 | ** Initializes some JSON bits which need to be initialized relatively |
| 715 | ** early on. It should only be called from cgi_init() or |
| 716 | ** json_cmd_top() (early on in those functions). |
| @@ -931,11 +942,11 @@ | |
| 931 | ** This must be called by the top-level JSON command dispatching code |
| 932 | ** before they do any work. |
| 933 | ** |
| 934 | ** This must only be called once, or an assertion may be triggered. |
| 935 | */ |
| 936 | static void json_mode_bootstrap(){ |
| 937 | static char once = 0 /* guard against multiple runs */; |
| 938 | char const * zPath = P("PATH_INFO"); |
| 939 | assert(g.json.gc.a && "json_main_bootstrap() was not called!"); |
| 940 | assert( (0==once) && "json_mode_bootstrap() called too many times!"); |
| 941 | if( once ){ |
| @@ -2262,11 +2273,11 @@ | |
| 2262 | ** json_cmd_top(). |
| 2263 | */ |
| 2264 | void json_page_top(void){ |
| 2265 | char const * zCommand; |
| 2266 | assert(g.json.gc.a && "json_main_bootstrap() was not called!"); |
| 2267 | json_mode_bootstrap(); |
| 2268 | zCommand = json_command_arg(1); |
| 2269 | if(!zCommand || !*zCommand){ |
| 2270 | json_dispatch_missing_args_err( JsonPageDefs, |
| 2271 | "No command (sub-path) specified." |
| 2272 | " Try one of: "); |
| 2273 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -707,10 +707,21 @@ | |
| 707 | cson_value * json_req_payload_get(char const *pKey){ |
| 708 | return g.json.reqPayload.o |
| 709 | ? cson_object_get(g.json.reqPayload.o,pKey) |
| 710 | : NULL; |
| 711 | } |
| 712 | |
| 713 | /* |
| 714 | ** Returns non-zero if the json_main_bootstrap() function has already |
| 715 | ** been called. In general, this function should be used sparingly, |
| 716 | ** e.g. from low-level support functions like fossil_warning() where |
| 717 | ** there is genuine uncertainty about whether (or not) the JSON setup |
| 718 | ** has already been called. |
| 719 | */ |
| 720 | int json_is_main_boostrapped(){ |
| 721 | return ((g.json.gc.v != NULL) && (g.json.gc.a != NULL)); |
| 722 | } |
| 723 | |
| 724 | /* |
| 725 | ** Initializes some JSON bits which need to be initialized relatively |
| 726 | ** early on. It should only be called from cgi_init() or |
| 727 | ** json_cmd_top() (early on in those functions). |
| @@ -931,11 +942,11 @@ | |
| 942 | ** This must be called by the top-level JSON command dispatching code |
| 943 | ** before they do any work. |
| 944 | ** |
| 945 | ** This must only be called once, or an assertion may be triggered. |
| 946 | */ |
| 947 | void json_mode_bootstrap(){ |
| 948 | static char once = 0 /* guard against multiple runs */; |
| 949 | char const * zPath = P("PATH_INFO"); |
| 950 | assert(g.json.gc.a && "json_main_bootstrap() was not called!"); |
| 951 | assert( (0==once) && "json_mode_bootstrap() called too many times!"); |
| 952 | if( once ){ |
| @@ -2262,11 +2273,11 @@ | |
| 2273 | ** json_cmd_top(). |
| 2274 | */ |
| 2275 | void json_page_top(void){ |
| 2276 | char const * zCommand; |
| 2277 | assert(g.json.gc.a && "json_main_bootstrap() was not called!"); |
| 2278 | assert(g.json.cmd.a && "json_mode_bootstrap() was not called!"); |
| 2279 | zCommand = json_command_arg(1); |
| 2280 | if(!zCommand || !*zCommand){ |
| 2281 | json_dispatch_missing_args_err( JsonPageDefs, |
| 2282 | "No command (sub-path) specified." |
| 2283 | " Try one of: "); |
| 2284 |
+13
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -274,10 +274,13 @@ | ||
| 274 | 274 | always output JSON-form error |
| 275 | 275 | responses and always (in CGI mode) |
| 276 | 276 | exit() with code 0 to avoid an HTTP |
| 277 | 277 | 500 error. |
| 278 | 278 | */ |
| 279 | + int preserveRc; /* Do not convert error codes into 0. | |
| 280 | + * This is primarily intended for use | |
| 281 | + * by the test suite. */ | |
| 279 | 282 | int resultCode; /* used for passing back specific codes |
| 280 | 283 | ** from /json callbacks. */ |
| 281 | 284 | int errorDetailParanoia; /* 0=full error codes, 1=%10, 2=%100, 3=%1000 */ |
| 282 | 285 | cson_output_opt outOpt; /* formatting options for JSON mode. */ |
| 283 | 286 | cson_value *authToken; /* authentication token */ |
| @@ -746,10 +749,13 @@ | ||
| 746 | 749 | g.fSshTrace = find_option("sshtrace", 0, 0)!=0; |
| 747 | 750 | g.fCgiTrace = find_option("cgitrace", 0, 0)!=0; |
| 748 | 751 | g.fSshClient = 0; |
| 749 | 752 | g.zSshCmd = 0; |
| 750 | 753 | if( g.fSqlTrace ) g.fSqlStats = 1; |
| 754 | +#ifdef FOSSIL_ENABLE_JSON | |
| 755 | + g.json.preserveRc = find_option("json-preserve-rc", 0, 0)!=0; | |
| 756 | +#endif | |
| 751 | 757 | g.fHttpTrace = find_option("httptrace", 0, 0)!=0; |
| 752 | 758 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 753 | 759 | g.fNoThHook = find_option("no-th-hook", 0, 0)!=0; |
| 754 | 760 | #endif |
| 755 | 761 | g.fAnyTrace = g.fSqlTrace|g.fSystemTrace|g.fSshTrace| |
| @@ -1909,10 +1915,17 @@ | ||
| 1909 | 1915 | @ <h1>Server Configuration Error</h1> |
| 1910 | 1916 | @ <p>The database schema on the server is out-of-date. Please ask |
| 1911 | 1917 | @ the administrator to run <b>fossil rebuild</b>.</p> |
| 1912 | 1918 | } |
| 1913 | 1919 | }else{ |
| 1920 | +#ifdef FOSSIL_ENABLE_JSON | |
| 1921 | + static int jsonOnce = 0; | |
| 1922 | + if( !jsonOnce && g.json.isJsonMode ){ | |
| 1923 | + json_mode_bootstrap(); | |
| 1924 | + jsonOnce = 1; | |
| 1925 | + } | |
| 1926 | +#endif | |
| 1914 | 1927 | if( (pCmd->eCmdFlags & CMDFLAG_RAWCONTENT)==0 ){ |
| 1915 | 1928 | cgi_decode_post_parameters(); |
| 1916 | 1929 | } |
| 1917 | 1930 | if( g.fCgiTrace ){ |
| 1918 | 1931 | fossil_trace("######## Calling %s #########\n", pCmd->zName); |
| 1919 | 1932 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -274,10 +274,13 @@ | |
| 274 | always output JSON-form error |
| 275 | responses and always (in CGI mode) |
| 276 | exit() with code 0 to avoid an HTTP |
| 277 | 500 error. |
| 278 | */ |
| 279 | int resultCode; /* used for passing back specific codes |
| 280 | ** from /json callbacks. */ |
| 281 | int errorDetailParanoia; /* 0=full error codes, 1=%10, 2=%100, 3=%1000 */ |
| 282 | cson_output_opt outOpt; /* formatting options for JSON mode. */ |
| 283 | cson_value *authToken; /* authentication token */ |
| @@ -746,10 +749,13 @@ | |
| 746 | g.fSshTrace = find_option("sshtrace", 0, 0)!=0; |
| 747 | g.fCgiTrace = find_option("cgitrace", 0, 0)!=0; |
| 748 | g.fSshClient = 0; |
| 749 | g.zSshCmd = 0; |
| 750 | if( g.fSqlTrace ) g.fSqlStats = 1; |
| 751 | g.fHttpTrace = find_option("httptrace", 0, 0)!=0; |
| 752 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 753 | g.fNoThHook = find_option("no-th-hook", 0, 0)!=0; |
| 754 | #endif |
| 755 | g.fAnyTrace = g.fSqlTrace|g.fSystemTrace|g.fSshTrace| |
| @@ -1909,10 +1915,17 @@ | |
| 1909 | @ <h1>Server Configuration Error</h1> |
| 1910 | @ <p>The database schema on the server is out-of-date. Please ask |
| 1911 | @ the administrator to run <b>fossil rebuild</b>.</p> |
| 1912 | } |
| 1913 | }else{ |
| 1914 | if( (pCmd->eCmdFlags & CMDFLAG_RAWCONTENT)==0 ){ |
| 1915 | cgi_decode_post_parameters(); |
| 1916 | } |
| 1917 | if( g.fCgiTrace ){ |
| 1918 | fossil_trace("######## Calling %s #########\n", pCmd->zName); |
| 1919 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -274,10 +274,13 @@ | |
| 274 | always output JSON-form error |
| 275 | responses and always (in CGI mode) |
| 276 | exit() with code 0 to avoid an HTTP |
| 277 | 500 error. |
| 278 | */ |
| 279 | int preserveRc; /* Do not convert error codes into 0. |
| 280 | * This is primarily intended for use |
| 281 | * by the test suite. */ |
| 282 | int resultCode; /* used for passing back specific codes |
| 283 | ** from /json callbacks. */ |
| 284 | int errorDetailParanoia; /* 0=full error codes, 1=%10, 2=%100, 3=%1000 */ |
| 285 | cson_output_opt outOpt; /* formatting options for JSON mode. */ |
| 286 | cson_value *authToken; /* authentication token */ |
| @@ -746,10 +749,13 @@ | |
| 749 | g.fSshTrace = find_option("sshtrace", 0, 0)!=0; |
| 750 | g.fCgiTrace = find_option("cgitrace", 0, 0)!=0; |
| 751 | g.fSshClient = 0; |
| 752 | g.zSshCmd = 0; |
| 753 | if( g.fSqlTrace ) g.fSqlStats = 1; |
| 754 | #ifdef FOSSIL_ENABLE_JSON |
| 755 | g.json.preserveRc = find_option("json-preserve-rc", 0, 0)!=0; |
| 756 | #endif |
| 757 | g.fHttpTrace = find_option("httptrace", 0, 0)!=0; |
| 758 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 759 | g.fNoThHook = find_option("no-th-hook", 0, 0)!=0; |
| 760 | #endif |
| 761 | g.fAnyTrace = g.fSqlTrace|g.fSystemTrace|g.fSshTrace| |
| @@ -1909,10 +1915,17 @@ | |
| 1915 | @ <h1>Server Configuration Error</h1> |
| 1916 | @ <p>The database schema on the server is out-of-date. Please ask |
| 1917 | @ the administrator to run <b>fossil rebuild</b>.</p> |
| 1918 | } |
| 1919 | }else{ |
| 1920 | #ifdef FOSSIL_ENABLE_JSON |
| 1921 | static int jsonOnce = 0; |
| 1922 | if( !jsonOnce && g.json.isJsonMode ){ |
| 1923 | json_mode_bootstrap(); |
| 1924 | jsonOnce = 1; |
| 1925 | } |
| 1926 | #endif |
| 1927 | if( (pCmd->eCmdFlags & CMDFLAG_RAWCONTENT)==0 ){ |
| 1928 | cgi_decode_post_parameters(); |
| 1929 | } |
| 1930 | if( g.fCgiTrace ){ |
| 1931 | fossil_trace("######## Calling %s #########\n", pCmd->zName); |
| 1932 |
+17
-1
| --- src/printf.c | ||
| +++ src/printf.c | ||
| @@ -1076,13 +1076,23 @@ | ||
| 1076 | 1076 | ** Write error message output |
| 1077 | 1077 | */ |
| 1078 | 1078 | static int fossil_print_error(int rc, const char *z){ |
| 1079 | 1079 | #ifdef FOSSIL_ENABLE_JSON |
| 1080 | 1080 | if( g.json.isJsonMode ){ |
| 1081 | + /* | |
| 1082 | + ** Avoid calling into the JSON support subsystem if it | |
| 1083 | + ** has not yet been initialized, e.g. early SQLite log | |
| 1084 | + ** messages, etc. | |
| 1085 | + */ | |
| 1086 | + if( !json_is_main_boostrapped() ) json_main_bootstrap(); | |
| 1081 | 1087 | json_err( 0, z, 1 ); |
| 1082 | - if( g.isHTTP ){ | |
| 1088 | + if( g.isHTTP && !g.json.preserveRc ){ | |
| 1083 | 1089 | rc = 0 /* avoid HTTP 500 */; |
| 1090 | + } | |
| 1091 | + if( g.cgiOutput==1 ){ | |
| 1092 | + g.cgiOutput = 2; | |
| 1093 | + cgi_reply(); | |
| 1084 | 1094 | } |
| 1085 | 1095 | } |
| 1086 | 1096 | else |
| 1087 | 1097 | #endif |
| 1088 | 1098 | if( g.cgiOutput==1 && g.db ){ |
| @@ -1191,10 +1201,16 @@ | ||
| 1191 | 1201 | z = vmprintf(zFormat, ap); |
| 1192 | 1202 | va_end(ap); |
| 1193 | 1203 | fossil_errorlog("warning: %s", z); |
| 1194 | 1204 | #ifdef FOSSIL_ENABLE_JSON |
| 1195 | 1205 | if(g.json.isJsonMode){ |
| 1206 | + /* | |
| 1207 | + ** Avoid calling into the JSON support subsystem if it | |
| 1208 | + ** has not yet been initialized, e.g. early SQLite log | |
| 1209 | + ** messages, etc. | |
| 1210 | + */ | |
| 1211 | + if( !json_is_main_boostrapped() ) json_main_bootstrap(); | |
| 1196 | 1212 | json_warn( FSL_JSON_W_UNKNOWN, "%s", z ); |
| 1197 | 1213 | }else |
| 1198 | 1214 | #endif |
| 1199 | 1215 | { |
| 1200 | 1216 | if( g.cgiOutput==1 ){ |
| 1201 | 1217 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -1076,13 +1076,23 @@ | |
| 1076 | ** Write error message output |
| 1077 | */ |
| 1078 | static int fossil_print_error(int rc, const char *z){ |
| 1079 | #ifdef FOSSIL_ENABLE_JSON |
| 1080 | if( g.json.isJsonMode ){ |
| 1081 | json_err( 0, z, 1 ); |
| 1082 | if( g.isHTTP ){ |
| 1083 | rc = 0 /* avoid HTTP 500 */; |
| 1084 | } |
| 1085 | } |
| 1086 | else |
| 1087 | #endif |
| 1088 | if( g.cgiOutput==1 && g.db ){ |
| @@ -1191,10 +1201,16 @@ | |
| 1191 | z = vmprintf(zFormat, ap); |
| 1192 | va_end(ap); |
| 1193 | fossil_errorlog("warning: %s", z); |
| 1194 | #ifdef FOSSIL_ENABLE_JSON |
| 1195 | if(g.json.isJsonMode){ |
| 1196 | json_warn( FSL_JSON_W_UNKNOWN, "%s", z ); |
| 1197 | }else |
| 1198 | #endif |
| 1199 | { |
| 1200 | if( g.cgiOutput==1 ){ |
| 1201 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -1076,13 +1076,23 @@ | |
| 1076 | ** Write error message output |
| 1077 | */ |
| 1078 | static int fossil_print_error(int rc, const char *z){ |
| 1079 | #ifdef FOSSIL_ENABLE_JSON |
| 1080 | if( g.json.isJsonMode ){ |
| 1081 | /* |
| 1082 | ** Avoid calling into the JSON support subsystem if it |
| 1083 | ** has not yet been initialized, e.g. early SQLite log |
| 1084 | ** messages, etc. |
| 1085 | */ |
| 1086 | if( !json_is_main_boostrapped() ) json_main_bootstrap(); |
| 1087 | json_err( 0, z, 1 ); |
| 1088 | if( g.isHTTP && !g.json.preserveRc ){ |
| 1089 | rc = 0 /* avoid HTTP 500 */; |
| 1090 | } |
| 1091 | if( g.cgiOutput==1 ){ |
| 1092 | g.cgiOutput = 2; |
| 1093 | cgi_reply(); |
| 1094 | } |
| 1095 | } |
| 1096 | else |
| 1097 | #endif |
| 1098 | if( g.cgiOutput==1 && g.db ){ |
| @@ -1191,10 +1201,16 @@ | |
| 1201 | z = vmprintf(zFormat, ap); |
| 1202 | va_end(ap); |
| 1203 | fossil_errorlog("warning: %s", z); |
| 1204 | #ifdef FOSSIL_ENABLE_JSON |
| 1205 | if(g.json.isJsonMode){ |
| 1206 | /* |
| 1207 | ** Avoid calling into the JSON support subsystem if it |
| 1208 | ** has not yet been initialized, e.g. early SQLite log |
| 1209 | ** messages, etc. |
| 1210 | */ |
| 1211 | if( !json_is_main_boostrapped() ) json_main_bootstrap(); |
| 1212 | json_warn( FSL_JSON_W_UNKNOWN, "%s", z ); |
| 1213 | }else |
| 1214 | #endif |
| 1215 | { |
| 1216 | if( g.cgiOutput==1 ){ |
| 1217 |
+1
| --- src/th_main.c | ||
| +++ src/th_main.c | ||
| @@ -128,10 +128,11 @@ | ||
| 128 | 128 | ** it for use. |
| 129 | 129 | */ |
| 130 | 130 | void Th_InitTraceLog(){ |
| 131 | 131 | g.thTrace = find_option("th-trace", 0, 0)!=0; |
| 132 | 132 | if( g.thTrace ){ |
| 133 | + g.fAnyTrace = 1; | |
| 133 | 134 | blob_zero(&g.thLog); |
| 134 | 135 | } |
| 135 | 136 | } |
| 136 | 137 | |
| 137 | 138 | /* |
| 138 | 139 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -128,10 +128,11 @@ | |
| 128 | ** it for use. |
| 129 | */ |
| 130 | void Th_InitTraceLog(){ |
| 131 | g.thTrace = find_option("th-trace", 0, 0)!=0; |
| 132 | if( g.thTrace ){ |
| 133 | blob_zero(&g.thLog); |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | /* |
| 138 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -128,10 +128,11 @@ | |
| 128 | ** it for use. |
| 129 | */ |
| 130 | void Th_InitTraceLog(){ |
| 131 | g.thTrace = find_option("th-trace", 0, 0)!=0; |
| 132 | if( g.thTrace ){ |
| 133 | g.fAnyTrace = 1; |
| 134 | blob_zero(&g.thLog); |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | /* |
| 139 |
+50
-21
| --- test/json.test | ||
| +++ test/json.test | ||
| @@ -33,11 +33,14 @@ | ||
| 33 | 33 | # We need a JSON parser to effectively test the JSON produced by |
| 34 | 34 | # fossil. It looks like the one from tcllib is exactly what we need. |
| 35 | 35 | # On ActiveTcl, add it with teacup. On other platforms, YMMV. |
| 36 | 36 | # teacup install json |
| 37 | 37 | # teacup install json::write |
| 38 | -package require json | |
| 38 | +if {[catch {package require json}] != 0} then { | |
| 39 | + puts "The \"json\" package is not available." | |
| 40 | + test_cleanup_then_return | |
| 41 | +} | |
| 39 | 42 | |
| 40 | 43 | proc json2dict {txt} { |
| 41 | 44 | set rc [catch {::json::json2dict $txt} result options] |
| 42 | 45 | if {$rc != 0} { |
| 43 | 46 | protOut "JSON ERROR: $result" |
| @@ -74,11 +77,12 @@ | ||
| 74 | 77 | # |
| 75 | 78 | # Returns the status code from the HTTP header. |
| 76 | 79 | proc fossil_http_json {url {cookie "Muppet=Monster"} args} { |
| 77 | 80 | global RESULT JR |
| 78 | 81 | set request "GET $url HTTP/1.1\r\nHost: localhost\r\nUser-Agent: Fossil-http-json\r\nCookie: $cookie" |
| 79 | - set RESULT [fossil_maybe_answer $request http {*}$args] | |
| 82 | + set RESULT [fossil_maybe_answer $request http {*}$args --ipaddr 127.0.0.1] | |
| 83 | + set head ""; set body ""; set status "--NO_MATCH--" | |
| 80 | 84 | regexp {(?w)(.*)^\s*$(.*)} $RESULT dummy head body |
| 81 | 85 | regexp {^HTTP\S+\s+(\d\d\d)\s+(.*)$} $head dummy status msg |
| 82 | 86 | if {$status eq "200"} { |
| 83 | 87 | set JR [json2dict $body] |
| 84 | 88 | } |
| @@ -115,13 +119,14 @@ | ||
| 115 | 119 | } |
| 116 | 120 | |
| 117 | 121 | # handle the actual request |
| 118 | 122 | flush stdout |
| 119 | 123 | #exec $fossilexe |
| 120 | - set RESULT [fossil_maybe_answer $request http {*}$args] | |
| 124 | + set RESULT [fossil_maybe_answer $request http {*}$args --ipaddr 127.0.0.1] | |
| 121 | 125 | |
| 122 | 126 | # separate HTTP headers from body |
| 127 | + set head ""; set body ""; set status "--NO_MATCH--" | |
| 123 | 128 | regexp {(?w)(.*)^\s*$(.*)} $RESULT dummy head body |
| 124 | 129 | regexp {^HTTP\S+\s+(\d\d\d)\s+(.*)$} $head dummy status msg |
| 125 | 130 | if {$status eq "200"} { |
| 126 | 131 | if {[string length $body] > 0} { |
| 127 | 132 | set JR [json2dict $body] |
| @@ -170,10 +175,13 @@ | ||
| 170 | 175 | |
| 171 | 176 | #### VERSION AKA HAI |
| 172 | 177 | |
| 173 | 178 | # The JSON API generally assumes we have a respository, so let it have one. |
| 174 | 179 | test_setup |
| 180 | + | |
| 181 | +# Stop backoffice from running during this test as it can cause hangs. | |
| 182 | +fossil settings backoffice-disable 1 | |
| 175 | 183 | |
| 176 | 184 | # Check for basic envelope fields in the result with an error |
| 177 | 185 | fossil_json -expectError |
| 178 | 186 | test_json_envelope json-enverr [concat resultCode fossil timestamp \ |
| 179 | 187 | resultText command procTimeUs procTimeMs] {} |
| @@ -307,28 +315,40 @@ | ||
| 307 | 315 | # json cap via POST with authToken in request envelope |
| 308 | 316 | set anon2 [read_file anon-2] |
| 309 | 317 | fossil_post_json "/json/cap" $anon2 |
| 310 | 318 | test json-cap-POSTenv-env-0 {[string length $JR] > 0} |
| 311 | 319 | test_json_envelope_ok json-cap-POSTenv-env |
| 312 | -test json-cap-POSTenv-name {[dict get $JR payload name] eq "anonymous"} knownBug | |
| 320 | +if {[catch {test json-cap-POSTenv-name \ | |
| 321 | + {[dict get $JR payload name] eq "anonymous"} knownBug} jerr]} then { | |
| 322 | + test json-cap-POSTenv-name-threw 0 | |
| 323 | + protOut "CAUGHT: $jerr" | |
| 324 | +} | |
| 313 | 325 | test json-cap-POSTenv-notsetup {![dict get $JR payload permissionFlags setup]} |
| 314 | 326 | |
| 315 | 327 | |
| 316 | 328 | # json cap via GET with authToken in Cookie header |
| 317 | 329 | fossil_post_json "/json/cap" {} $AnonCookie |
| 318 | 330 | test json-cap-GETcookie-env-0 {[string length $JR] > 0} |
| 319 | -test_json_envelope_ok json-cap-GETcookie-env | |
| 320 | -test json-cap-GETcookie-name {[dict get $JR payload name] eq "anonymous"} | |
| 321 | -test json-cap-GETcookie-notsetup {![dict get $JR payload permissionFlags setup]} | |
| 331 | +test_json_envelope_ok json-cap-GETcookie-env-0 | |
| 332 | +if {[catch {test json-cap-GETcookie-name-0 \ | |
| 333 | + {[dict get $JR payload name] eq "anonymous"}} jerr]} then { | |
| 334 | + test json-cap-GETcookie-name-0-threw 0 | |
| 335 | + protOut "CAUGHT: $jerr" | |
| 336 | +} | |
| 337 | +test json-cap-GETcookie-notsetup-0 {![dict get $JR payload permissionFlags setup]} | |
| 322 | 338 | |
| 323 | 339 | |
| 324 | 340 | # json cap via GET with authToken in a parameter |
| 325 | 341 | fossil_post_json "/json/cap?authToken=[dict get $AuthAnon authToken]" {} |
| 326 | -test json-cap-GETcookie-env-0 {[string length $JR] > 0} | |
| 327 | -test_json_envelope_ok json-cap-GETcookie-env | |
| 328 | -test json-cap-GETcookie-name {[dict get $JR payload name] eq "anonymous"} | |
| 329 | -test json-cap-GETcookie-notsetup {![dict get $JR payload permissionFlags setup]} | |
| 342 | +test json-cap-GETcookie-env-1 {[string length $JR] > 0} | |
| 343 | +test_json_envelope_ok json-cap-GETcookie-env-1 | |
| 344 | +if {[catch {test json-cap-GETcookie-name-1 \ | |
| 345 | + {[dict get $JR payload name] eq "anonymous"}} jerr]} then { | |
| 346 | + test json-cap-GETcookie-name-1-threw 0 | |
| 347 | + protOut "CAUGHT: $jerr" | |
| 348 | +} | |
| 349 | +test json-cap-GETcookie-notsetup-1 {![dict get $JR payload permissionFlags setup]} | |
| 330 | 350 | |
| 331 | 351 | |
| 332 | 352 | # whoami |
| 333 | 353 | # via CLI with no auth token supplied |
| 334 | 354 | fossil_json whoami |
| @@ -675,27 +695,36 @@ | ||
| 675 | 695 | # error happens before we have made the determination that the app is |
| 676 | 696 | # in JSON mode or if the error handling is incorrectly not |
| 677 | 697 | # recognizing JSON mode. |
| 678 | 698 | # |
| 679 | 699 | #test_setup x.fossil |
| 680 | -#catch {exec chmod 444 .rep.fossil}; # Unix. What about Win? | |
| 681 | -fossil_http_json /json/timeline/checkin $U1Cookie | |
| 700 | +fossil_http_json /json/query?sql=PRAGMA%20repository.journal_mode%3Dwal $U1Cookie | |
| 682 | 701 | test json-ROrepo-1-1 {$CODE == 0} |
| 683 | 702 | test json-ROrepo-1-2 {[regexp {\}\s*$} $RESULT]} |
| 684 | 703 | test json-ROrepo-1-3 {![regexp {SQLITE_[A-Z]+:} $RESULT]} |
| 685 | 704 | test_json_envelope_ok json-http-timeline1 |
| 705 | +if {$is_windows} then { | |
| 706 | + catch {exec attrib +r .rep.fossil}; # Windows | |
| 707 | +} else { | |
| 708 | + catch {exec chmod 444 .rep.fossil}; # Unix | |
| 709 | +} | |
| 686 | 710 | protOut "chmod 444 repo" |
| 687 | -catch {exec chmod 444 .rep.fossil}; # Unix | |
| 688 | -catch {exec attrib +r .rep.fossil}; # Windows | |
| 689 | -fossil_http_json /json/timeline/checkin $U1Cookie -expectError | |
| 711 | +fossil_http_json /json/query?sql=PRAGMA%20repository.journal_mode%3Ddelete $U1Cookie -expectError --json-preserve-rc | |
| 690 | 712 | test json-ROrepo-2-1 {$CODE != 0} |
| 691 | -test json-ROrepo-2-2 {[regexp {\}\s*$} $RESULT]} knownBug | |
| 692 | -test json-ROrepo-2-3 {![regexp {SQLITE_[A-Z]+:} $RESULT]} knownBug | |
| 713 | +test json-ROrepo-2-2 {[regexp {\}\s*$} $RESULT]} | |
| 714 | +test json-ROrepo-2-3 {![regexp {SQLITE_[A-Z]+:} $RESULT]} | |
| 693 | 715 | #test_json_envelope_ok json-http-timeline2 |
| 694 | -catch {exec attrib -r .rep.fossil}; # Windows | |
| 695 | -catch {exec chmod 666 .rep.fossil}; # Unix | |
| 696 | - | |
| 716 | +if {$is_windows} then { | |
| 717 | + catch {exec attrib -r .rep.fossil}; # Windows | |
| 718 | + catch {exec attrib -r .rep.fossil-shm} | |
| 719 | + catch {exec attrib -r .rep.fossil-wal} | |
| 720 | +} else { | |
| 721 | + catch {exec chmod 666 .rep.fossil}; # Unix | |
| 722 | + catch {exec chmod 666 .rep.fossil-shm} | |
| 723 | + catch {exec chmod 666 .rep.fossil-wal} | |
| 724 | +} | |
| 725 | +protOut "chmod 666 repo" | |
| 697 | 726 | |
| 698 | 727 | #### Result Codes |
| 699 | 728 | # Test cases designed to stimulate each (documented) error code. |
| 700 | 729 | |
| 701 | 730 | # FOSSIL-0000 |
| 702 | 731 |
| --- test/json.test | |
| +++ test/json.test | |
| @@ -33,11 +33,14 @@ | |
| 33 | # We need a JSON parser to effectively test the JSON produced by |
| 34 | # fossil. It looks like the one from tcllib is exactly what we need. |
| 35 | # On ActiveTcl, add it with teacup. On other platforms, YMMV. |
| 36 | # teacup install json |
| 37 | # teacup install json::write |
| 38 | package require json |
| 39 | |
| 40 | proc json2dict {txt} { |
| 41 | set rc [catch {::json::json2dict $txt} result options] |
| 42 | if {$rc != 0} { |
| 43 | protOut "JSON ERROR: $result" |
| @@ -74,11 +77,12 @@ | |
| 74 | # |
| 75 | # Returns the status code from the HTTP header. |
| 76 | proc fossil_http_json {url {cookie "Muppet=Monster"} args} { |
| 77 | global RESULT JR |
| 78 | set request "GET $url HTTP/1.1\r\nHost: localhost\r\nUser-Agent: Fossil-http-json\r\nCookie: $cookie" |
| 79 | set RESULT [fossil_maybe_answer $request http {*}$args] |
| 80 | regexp {(?w)(.*)^\s*$(.*)} $RESULT dummy head body |
| 81 | regexp {^HTTP\S+\s+(\d\d\d)\s+(.*)$} $head dummy status msg |
| 82 | if {$status eq "200"} { |
| 83 | set JR [json2dict $body] |
| 84 | } |
| @@ -115,13 +119,14 @@ | |
| 115 | } |
| 116 | |
| 117 | # handle the actual request |
| 118 | flush stdout |
| 119 | #exec $fossilexe |
| 120 | set RESULT [fossil_maybe_answer $request http {*}$args] |
| 121 | |
| 122 | # separate HTTP headers from body |
| 123 | regexp {(?w)(.*)^\s*$(.*)} $RESULT dummy head body |
| 124 | regexp {^HTTP\S+\s+(\d\d\d)\s+(.*)$} $head dummy status msg |
| 125 | if {$status eq "200"} { |
| 126 | if {[string length $body] > 0} { |
| 127 | set JR [json2dict $body] |
| @@ -170,10 +175,13 @@ | |
| 170 | |
| 171 | #### VERSION AKA HAI |
| 172 | |
| 173 | # The JSON API generally assumes we have a respository, so let it have one. |
| 174 | test_setup |
| 175 | |
| 176 | # Check for basic envelope fields in the result with an error |
| 177 | fossil_json -expectError |
| 178 | test_json_envelope json-enverr [concat resultCode fossil timestamp \ |
| 179 | resultText command procTimeUs procTimeMs] {} |
| @@ -307,28 +315,40 @@ | |
| 307 | # json cap via POST with authToken in request envelope |
| 308 | set anon2 [read_file anon-2] |
| 309 | fossil_post_json "/json/cap" $anon2 |
| 310 | test json-cap-POSTenv-env-0 {[string length $JR] > 0} |
| 311 | test_json_envelope_ok json-cap-POSTenv-env |
| 312 | test json-cap-POSTenv-name {[dict get $JR payload name] eq "anonymous"} knownBug |
| 313 | test json-cap-POSTenv-notsetup {![dict get $JR payload permissionFlags setup]} |
| 314 | |
| 315 | |
| 316 | # json cap via GET with authToken in Cookie header |
| 317 | fossil_post_json "/json/cap" {} $AnonCookie |
| 318 | test json-cap-GETcookie-env-0 {[string length $JR] > 0} |
| 319 | test_json_envelope_ok json-cap-GETcookie-env |
| 320 | test json-cap-GETcookie-name {[dict get $JR payload name] eq "anonymous"} |
| 321 | test json-cap-GETcookie-notsetup {![dict get $JR payload permissionFlags setup]} |
| 322 | |
| 323 | |
| 324 | # json cap via GET with authToken in a parameter |
| 325 | fossil_post_json "/json/cap?authToken=[dict get $AuthAnon authToken]" {} |
| 326 | test json-cap-GETcookie-env-0 {[string length $JR] > 0} |
| 327 | test_json_envelope_ok json-cap-GETcookie-env |
| 328 | test json-cap-GETcookie-name {[dict get $JR payload name] eq "anonymous"} |
| 329 | test json-cap-GETcookie-notsetup {![dict get $JR payload permissionFlags setup]} |
| 330 | |
| 331 | |
| 332 | # whoami |
| 333 | # via CLI with no auth token supplied |
| 334 | fossil_json whoami |
| @@ -675,27 +695,36 @@ | |
| 675 | # error happens before we have made the determination that the app is |
| 676 | # in JSON mode or if the error handling is incorrectly not |
| 677 | # recognizing JSON mode. |
| 678 | # |
| 679 | #test_setup x.fossil |
| 680 | #catch {exec chmod 444 .rep.fossil}; # Unix. What about Win? |
| 681 | fossil_http_json /json/timeline/checkin $U1Cookie |
| 682 | test json-ROrepo-1-1 {$CODE == 0} |
| 683 | test json-ROrepo-1-2 {[regexp {\}\s*$} $RESULT]} |
| 684 | test json-ROrepo-1-3 {![regexp {SQLITE_[A-Z]+:} $RESULT]} |
| 685 | test_json_envelope_ok json-http-timeline1 |
| 686 | protOut "chmod 444 repo" |
| 687 | catch {exec chmod 444 .rep.fossil}; # Unix |
| 688 | catch {exec attrib +r .rep.fossil}; # Windows |
| 689 | fossil_http_json /json/timeline/checkin $U1Cookie -expectError |
| 690 | test json-ROrepo-2-1 {$CODE != 0} |
| 691 | test json-ROrepo-2-2 {[regexp {\}\s*$} $RESULT]} knownBug |
| 692 | test json-ROrepo-2-3 {![regexp {SQLITE_[A-Z]+:} $RESULT]} knownBug |
| 693 | #test_json_envelope_ok json-http-timeline2 |
| 694 | catch {exec attrib -r .rep.fossil}; # Windows |
| 695 | catch {exec chmod 666 .rep.fossil}; # Unix |
| 696 | |
| 697 | |
| 698 | #### Result Codes |
| 699 | # Test cases designed to stimulate each (documented) error code. |
| 700 | |
| 701 | # FOSSIL-0000 |
| 702 |
| --- test/json.test | |
| +++ test/json.test | |
| @@ -33,11 +33,14 @@ | |
| 33 | # We need a JSON parser to effectively test the JSON produced by |
| 34 | # fossil. It looks like the one from tcllib is exactly what we need. |
| 35 | # On ActiveTcl, add it with teacup. On other platforms, YMMV. |
| 36 | # teacup install json |
| 37 | # teacup install json::write |
| 38 | if {[catch {package require json}] != 0} then { |
| 39 | puts "The \"json\" package is not available." |
| 40 | test_cleanup_then_return |
| 41 | } |
| 42 | |
| 43 | proc json2dict {txt} { |
| 44 | set rc [catch {::json::json2dict $txt} result options] |
| 45 | if {$rc != 0} { |
| 46 | protOut "JSON ERROR: $result" |
| @@ -74,11 +77,12 @@ | |
| 77 | # |
| 78 | # Returns the status code from the HTTP header. |
| 79 | proc fossil_http_json {url {cookie "Muppet=Monster"} args} { |
| 80 | global RESULT JR |
| 81 | set request "GET $url HTTP/1.1\r\nHost: localhost\r\nUser-Agent: Fossil-http-json\r\nCookie: $cookie" |
| 82 | set RESULT [fossil_maybe_answer $request http {*}$args --ipaddr 127.0.0.1] |
| 83 | set head ""; set body ""; set status "--NO_MATCH--" |
| 84 | regexp {(?w)(.*)^\s*$(.*)} $RESULT dummy head body |
| 85 | regexp {^HTTP\S+\s+(\d\d\d)\s+(.*)$} $head dummy status msg |
| 86 | if {$status eq "200"} { |
| 87 | set JR [json2dict $body] |
| 88 | } |
| @@ -115,13 +119,14 @@ | |
| 119 | } |
| 120 | |
| 121 | # handle the actual request |
| 122 | flush stdout |
| 123 | #exec $fossilexe |
| 124 | set RESULT [fossil_maybe_answer $request http {*}$args --ipaddr 127.0.0.1] |
| 125 | |
| 126 | # separate HTTP headers from body |
| 127 | set head ""; set body ""; set status "--NO_MATCH--" |
| 128 | regexp {(?w)(.*)^\s*$(.*)} $RESULT dummy head body |
| 129 | regexp {^HTTP\S+\s+(\d\d\d)\s+(.*)$} $head dummy status msg |
| 130 | if {$status eq "200"} { |
| 131 | if {[string length $body] > 0} { |
| 132 | set JR [json2dict $body] |
| @@ -170,10 +175,13 @@ | |
| 175 | |
| 176 | #### VERSION AKA HAI |
| 177 | |
| 178 | # The JSON API generally assumes we have a respository, so let it have one. |
| 179 | test_setup |
| 180 | |
| 181 | # Stop backoffice from running during this test as it can cause hangs. |
| 182 | fossil settings backoffice-disable 1 |
| 183 | |
| 184 | # Check for basic envelope fields in the result with an error |
| 185 | fossil_json -expectError |
| 186 | test_json_envelope json-enverr [concat resultCode fossil timestamp \ |
| 187 | resultText command procTimeUs procTimeMs] {} |
| @@ -307,28 +315,40 @@ | |
| 315 | # json cap via POST with authToken in request envelope |
| 316 | set anon2 [read_file anon-2] |
| 317 | fossil_post_json "/json/cap" $anon2 |
| 318 | test json-cap-POSTenv-env-0 {[string length $JR] > 0} |
| 319 | test_json_envelope_ok json-cap-POSTenv-env |
| 320 | if {[catch {test json-cap-POSTenv-name \ |
| 321 | {[dict get $JR payload name] eq "anonymous"} knownBug} jerr]} then { |
| 322 | test json-cap-POSTenv-name-threw 0 |
| 323 | protOut "CAUGHT: $jerr" |
| 324 | } |
| 325 | test json-cap-POSTenv-notsetup {![dict get $JR payload permissionFlags setup]} |
| 326 | |
| 327 | |
| 328 | # json cap via GET with authToken in Cookie header |
| 329 | fossil_post_json "/json/cap" {} $AnonCookie |
| 330 | test json-cap-GETcookie-env-0 {[string length $JR] > 0} |
| 331 | test_json_envelope_ok json-cap-GETcookie-env-0 |
| 332 | if {[catch {test json-cap-GETcookie-name-0 \ |
| 333 | {[dict get $JR payload name] eq "anonymous"}} jerr]} then { |
| 334 | test json-cap-GETcookie-name-0-threw 0 |
| 335 | protOut "CAUGHT: $jerr" |
| 336 | } |
| 337 | test json-cap-GETcookie-notsetup-0 {![dict get $JR payload permissionFlags setup]} |
| 338 | |
| 339 | |
| 340 | # json cap via GET with authToken in a parameter |
| 341 | fossil_post_json "/json/cap?authToken=[dict get $AuthAnon authToken]" {} |
| 342 | test json-cap-GETcookie-env-1 {[string length $JR] > 0} |
| 343 | test_json_envelope_ok json-cap-GETcookie-env-1 |
| 344 | if {[catch {test json-cap-GETcookie-name-1 \ |
| 345 | {[dict get $JR payload name] eq "anonymous"}} jerr]} then { |
| 346 | test json-cap-GETcookie-name-1-threw 0 |
| 347 | protOut "CAUGHT: $jerr" |
| 348 | } |
| 349 | test json-cap-GETcookie-notsetup-1 {![dict get $JR payload permissionFlags setup]} |
| 350 | |
| 351 | |
| 352 | # whoami |
| 353 | # via CLI with no auth token supplied |
| 354 | fossil_json whoami |
| @@ -675,27 +695,36 @@ | |
| 695 | # error happens before we have made the determination that the app is |
| 696 | # in JSON mode or if the error handling is incorrectly not |
| 697 | # recognizing JSON mode. |
| 698 | # |
| 699 | #test_setup x.fossil |
| 700 | fossil_http_json /json/query?sql=PRAGMA%20repository.journal_mode%3Dwal $U1Cookie |
| 701 | test json-ROrepo-1-1 {$CODE == 0} |
| 702 | test json-ROrepo-1-2 {[regexp {\}\s*$} $RESULT]} |
| 703 | test json-ROrepo-1-3 {![regexp {SQLITE_[A-Z]+:} $RESULT]} |
| 704 | test_json_envelope_ok json-http-timeline1 |
| 705 | if {$is_windows} then { |
| 706 | catch {exec attrib +r .rep.fossil}; # Windows |
| 707 | } else { |
| 708 | catch {exec chmod 444 .rep.fossil}; # Unix |
| 709 | } |
| 710 | protOut "chmod 444 repo" |
| 711 | fossil_http_json /json/query?sql=PRAGMA%20repository.journal_mode%3Ddelete $U1Cookie -expectError --json-preserve-rc |
| 712 | test json-ROrepo-2-1 {$CODE != 0} |
| 713 | test json-ROrepo-2-2 {[regexp {\}\s*$} $RESULT]} |
| 714 | test json-ROrepo-2-3 {![regexp {SQLITE_[A-Z]+:} $RESULT]} |
| 715 | #test_json_envelope_ok json-http-timeline2 |
| 716 | if {$is_windows} then { |
| 717 | catch {exec attrib -r .rep.fossil}; # Windows |
| 718 | catch {exec attrib -r .rep.fossil-shm} |
| 719 | catch {exec attrib -r .rep.fossil-wal} |
| 720 | } else { |
| 721 | catch {exec chmod 666 .rep.fossil}; # Unix |
| 722 | catch {exec chmod 666 .rep.fossil-shm} |
| 723 | catch {exec chmod 666 .rep.fossil-wal} |
| 724 | } |
| 725 | protOut "chmod 666 repo" |
| 726 | |
| 727 | #### Result Codes |
| 728 | # Test cases designed to stimulate each (documented) error code. |
| 729 | |
| 730 | # FOSSIL-0000 |
| 731 |
+50
-21
| --- test/json.test | ||
| +++ test/json.test | ||
| @@ -33,11 +33,14 @@ | ||
| 33 | 33 | # We need a JSON parser to effectively test the JSON produced by |
| 34 | 34 | # fossil. It looks like the one from tcllib is exactly what we need. |
| 35 | 35 | # On ActiveTcl, add it with teacup. On other platforms, YMMV. |
| 36 | 36 | # teacup install json |
| 37 | 37 | # teacup install json::write |
| 38 | -package require json | |
| 38 | +if {[catch {package require json}] != 0} then { | |
| 39 | + puts "The \"json\" package is not available." | |
| 40 | + test_cleanup_then_return | |
| 41 | +} | |
| 39 | 42 | |
| 40 | 43 | proc json2dict {txt} { |
| 41 | 44 | set rc [catch {::json::json2dict $txt} result options] |
| 42 | 45 | if {$rc != 0} { |
| 43 | 46 | protOut "JSON ERROR: $result" |
| @@ -74,11 +77,12 @@ | ||
| 74 | 77 | # |
| 75 | 78 | # Returns the status code from the HTTP header. |
| 76 | 79 | proc fossil_http_json {url {cookie "Muppet=Monster"} args} { |
| 77 | 80 | global RESULT JR |
| 78 | 81 | set request "GET $url HTTP/1.1\r\nHost: localhost\r\nUser-Agent: Fossil-http-json\r\nCookie: $cookie" |
| 79 | - set RESULT [fossil_maybe_answer $request http {*}$args] | |
| 82 | + set RESULT [fossil_maybe_answer $request http {*}$args --ipaddr 127.0.0.1] | |
| 83 | + set head ""; set body ""; set status "--NO_MATCH--" | |
| 80 | 84 | regexp {(?w)(.*)^\s*$(.*)} $RESULT dummy head body |
| 81 | 85 | regexp {^HTTP\S+\s+(\d\d\d)\s+(.*)$} $head dummy status msg |
| 82 | 86 | if {$status eq "200"} { |
| 83 | 87 | set JR [json2dict $body] |
| 84 | 88 | } |
| @@ -115,13 +119,14 @@ | ||
| 115 | 119 | } |
| 116 | 120 | |
| 117 | 121 | # handle the actual request |
| 118 | 122 | flush stdout |
| 119 | 123 | #exec $fossilexe |
| 120 | - set RESULT [fossil_maybe_answer $request http {*}$args] | |
| 124 | + set RESULT [fossil_maybe_answer $request http {*}$args --ipaddr 127.0.0.1] | |
| 121 | 125 | |
| 122 | 126 | # separate HTTP headers from body |
| 127 | + set head ""; set body ""; set status "--NO_MATCH--" | |
| 123 | 128 | regexp {(?w)(.*)^\s*$(.*)} $RESULT dummy head body |
| 124 | 129 | regexp {^HTTP\S+\s+(\d\d\d)\s+(.*)$} $head dummy status msg |
| 125 | 130 | if {$status eq "200"} { |
| 126 | 131 | if {[string length $body] > 0} { |
| 127 | 132 | set JR [json2dict $body] |
| @@ -170,10 +175,13 @@ | ||
| 170 | 175 | |
| 171 | 176 | #### VERSION AKA HAI |
| 172 | 177 | |
| 173 | 178 | # The JSON API generally assumes we have a respository, so let it have one. |
| 174 | 179 | test_setup |
| 180 | + | |
| 181 | +# Stop backoffice from running during this test as it can cause hangs. | |
| 182 | +fossil settings backoffice-disable 1 | |
| 175 | 183 | |
| 176 | 184 | # Check for basic envelope fields in the result with an error |
| 177 | 185 | fossil_json -expectError |
| 178 | 186 | test_json_envelope json-enverr [concat resultCode fossil timestamp \ |
| 179 | 187 | resultText command procTimeUs procTimeMs] {} |
| @@ -307,28 +315,40 @@ | ||
| 307 | 315 | # json cap via POST with authToken in request envelope |
| 308 | 316 | set anon2 [read_file anon-2] |
| 309 | 317 | fossil_post_json "/json/cap" $anon2 |
| 310 | 318 | test json-cap-POSTenv-env-0 {[string length $JR] > 0} |
| 311 | 319 | test_json_envelope_ok json-cap-POSTenv-env |
| 312 | -test json-cap-POSTenv-name {[dict get $JR payload name] eq "anonymous"} knownBug | |
| 320 | +if {[catch {test json-cap-POSTenv-name \ | |
| 321 | + {[dict get $JR payload name] eq "anonymous"} knownBug} jerr]} then { | |
| 322 | + test json-cap-POSTenv-name-threw 0 | |
| 323 | + protOut "CAUGHT: $jerr" | |
| 324 | +} | |
| 313 | 325 | test json-cap-POSTenv-notsetup {![dict get $JR payload permissionFlags setup]} |
| 314 | 326 | |
| 315 | 327 | |
| 316 | 328 | # json cap via GET with authToken in Cookie header |
| 317 | 329 | fossil_post_json "/json/cap" {} $AnonCookie |
| 318 | 330 | test json-cap-GETcookie-env-0 {[string length $JR] > 0} |
| 319 | -test_json_envelope_ok json-cap-GETcookie-env | |
| 320 | -test json-cap-GETcookie-name {[dict get $JR payload name] eq "anonymous"} | |
| 321 | -test json-cap-GETcookie-notsetup {![dict get $JR payload permissionFlags setup]} | |
| 331 | +test_json_envelope_ok json-cap-GETcookie-env-0 | |
| 332 | +if {[catch {test json-cap-GETcookie-name-0 \ | |
| 333 | + {[dict get $JR payload name] eq "anonymous"}} jerr]} then { | |
| 334 | + test json-cap-GETcookie-name-0-threw 0 | |
| 335 | + protOut "CAUGHT: $jerr" | |
| 336 | +} | |
| 337 | +test json-cap-GETcookie-notsetup-0 {![dict get $JR payload permissionFlags setup]} | |
| 322 | 338 | |
| 323 | 339 | |
| 324 | 340 | # json cap via GET with authToken in a parameter |
| 325 | 341 | fossil_post_json "/json/cap?authToken=[dict get $AuthAnon authToken]" {} |
| 326 | -test json-cap-GETcookie-env-0 {[string length $JR] > 0} | |
| 327 | -test_json_envelope_ok json-cap-GETcookie-env | |
| 328 | -test json-cap-GETcookie-name {[dict get $JR payload name] eq "anonymous"} | |
| 329 | -test json-cap-GETcookie-notsetup {![dict get $JR payload permissionFlags setup]} | |
| 342 | +test json-cap-GETcookie-env-1 {[string length $JR] > 0} | |
| 343 | +test_json_envelope_ok json-cap-GETcookie-env-1 | |
| 344 | +if {[catch {test json-cap-GETcookie-name-1 \ | |
| 345 | + {[dict get $JR payload name] eq "anonymous"}} jerr]} then { | |
| 346 | + test json-cap-GETcookie-name-1-threw 0 | |
| 347 | + protOut "CAUGHT: $jerr" | |
| 348 | +} | |
| 349 | +test json-cap-GETcookie-notsetup-1 {![dict get $JR payload permissionFlags setup]} | |
| 330 | 350 | |
| 331 | 351 | |
| 332 | 352 | # whoami |
| 333 | 353 | # via CLI with no auth token supplied |
| 334 | 354 | fossil_json whoami |
| @@ -675,27 +695,36 @@ | ||
| 675 | 695 | # error happens before we have made the determination that the app is |
| 676 | 696 | # in JSON mode or if the error handling is incorrectly not |
| 677 | 697 | # recognizing JSON mode. |
| 678 | 698 | # |
| 679 | 699 | #test_setup x.fossil |
| 680 | -#catch {exec chmod 444 .rep.fossil}; # Unix. What about Win? | |
| 681 | -fossil_http_json /json/timeline/checkin $U1Cookie | |
| 700 | +fossil_http_json /json/query?sql=PRAGMA%20repository.journal_mode%3Dwal $U1Cookie | |
| 682 | 701 | test json-ROrepo-1-1 {$CODE == 0} |
| 683 | 702 | test json-ROrepo-1-2 {[regexp {\}\s*$} $RESULT]} |
| 684 | 703 | test json-ROrepo-1-3 {![regexp {SQLITE_[A-Z]+:} $RESULT]} |
| 685 | 704 | test_json_envelope_ok json-http-timeline1 |
| 705 | +if {$is_windows} then { | |
| 706 | + catch {exec attrib +r .rep.fossil}; # Windows | |
| 707 | +} else { | |
| 708 | + catch {exec chmod 444 .rep.fossil}; # Unix | |
| 709 | +} | |
| 686 | 710 | protOut "chmod 444 repo" |
| 687 | -catch {exec chmod 444 .rep.fossil}; # Unix | |
| 688 | -catch {exec attrib +r .rep.fossil}; # Windows | |
| 689 | -fossil_http_json /json/timeline/checkin $U1Cookie -expectError | |
| 711 | +fossil_http_json /json/query?sql=PRAGMA%20repository.journal_mode%3Ddelete $U1Cookie -expectError --json-preserve-rc | |
| 690 | 712 | test json-ROrepo-2-1 {$CODE != 0} |
| 691 | -test json-ROrepo-2-2 {[regexp {\}\s*$} $RESULT]} knownBug | |
| 692 | -test json-ROrepo-2-3 {![regexp {SQLITE_[A-Z]+:} $RESULT]} knownBug | |
| 713 | +test json-ROrepo-2-2 {[regexp {\}\s*$} $RESULT]} | |
| 714 | +test json-ROrepo-2-3 {![regexp {SQLITE_[A-Z]+:} $RESULT]} | |
| 693 | 715 | #test_json_envelope_ok json-http-timeline2 |
| 694 | -catch {exec attrib -r .rep.fossil}; # Windows | |
| 695 | -catch {exec chmod 666 .rep.fossil}; # Unix | |
| 696 | - | |
| 716 | +if {$is_windows} then { | |
| 717 | + catch {exec attrib -r .rep.fossil}; # Windows | |
| 718 | + catch {exec attrib -r .rep.fossil-shm} | |
| 719 | + catch {exec attrib -r .rep.fossil-wal} | |
| 720 | +} else { | |
| 721 | + catch {exec chmod 666 .rep.fossil}; # Unix | |
| 722 | + catch {exec chmod 666 .rep.fossil-shm} | |
| 723 | + catch {exec chmod 666 .rep.fossil-wal} | |
| 724 | +} | |
| 725 | +protOut "chmod 666 repo" | |
| 697 | 726 | |
| 698 | 727 | #### Result Codes |
| 699 | 728 | # Test cases designed to stimulate each (documented) error code. |
| 700 | 729 | |
| 701 | 730 | # FOSSIL-0000 |
| 702 | 731 |
| --- test/json.test | |
| +++ test/json.test | |
| @@ -33,11 +33,14 @@ | |
| 33 | # We need a JSON parser to effectively test the JSON produced by |
| 34 | # fossil. It looks like the one from tcllib is exactly what we need. |
| 35 | # On ActiveTcl, add it with teacup. On other platforms, YMMV. |
| 36 | # teacup install json |
| 37 | # teacup install json::write |
| 38 | package require json |
| 39 | |
| 40 | proc json2dict {txt} { |
| 41 | set rc [catch {::json::json2dict $txt} result options] |
| 42 | if {$rc != 0} { |
| 43 | protOut "JSON ERROR: $result" |
| @@ -74,11 +77,12 @@ | |
| 74 | # |
| 75 | # Returns the status code from the HTTP header. |
| 76 | proc fossil_http_json {url {cookie "Muppet=Monster"} args} { |
| 77 | global RESULT JR |
| 78 | set request "GET $url HTTP/1.1\r\nHost: localhost\r\nUser-Agent: Fossil-http-json\r\nCookie: $cookie" |
| 79 | set RESULT [fossil_maybe_answer $request http {*}$args] |
| 80 | regexp {(?w)(.*)^\s*$(.*)} $RESULT dummy head body |
| 81 | regexp {^HTTP\S+\s+(\d\d\d)\s+(.*)$} $head dummy status msg |
| 82 | if {$status eq "200"} { |
| 83 | set JR [json2dict $body] |
| 84 | } |
| @@ -115,13 +119,14 @@ | |
| 115 | } |
| 116 | |
| 117 | # handle the actual request |
| 118 | flush stdout |
| 119 | #exec $fossilexe |
| 120 | set RESULT [fossil_maybe_answer $request http {*}$args] |
| 121 | |
| 122 | # separate HTTP headers from body |
| 123 | regexp {(?w)(.*)^\s*$(.*)} $RESULT dummy head body |
| 124 | regexp {^HTTP\S+\s+(\d\d\d)\s+(.*)$} $head dummy status msg |
| 125 | if {$status eq "200"} { |
| 126 | if {[string length $body] > 0} { |
| 127 | set JR [json2dict $body] |
| @@ -170,10 +175,13 @@ | |
| 170 | |
| 171 | #### VERSION AKA HAI |
| 172 | |
| 173 | # The JSON API generally assumes we have a respository, so let it have one. |
| 174 | test_setup |
| 175 | |
| 176 | # Check for basic envelope fields in the result with an error |
| 177 | fossil_json -expectError |
| 178 | test_json_envelope json-enverr [concat resultCode fossil timestamp \ |
| 179 | resultText command procTimeUs procTimeMs] {} |
| @@ -307,28 +315,40 @@ | |
| 307 | # json cap via POST with authToken in request envelope |
| 308 | set anon2 [read_file anon-2] |
| 309 | fossil_post_json "/json/cap" $anon2 |
| 310 | test json-cap-POSTenv-env-0 {[string length $JR] > 0} |
| 311 | test_json_envelope_ok json-cap-POSTenv-env |
| 312 | test json-cap-POSTenv-name {[dict get $JR payload name] eq "anonymous"} knownBug |
| 313 | test json-cap-POSTenv-notsetup {![dict get $JR payload permissionFlags setup]} |
| 314 | |
| 315 | |
| 316 | # json cap via GET with authToken in Cookie header |
| 317 | fossil_post_json "/json/cap" {} $AnonCookie |
| 318 | test json-cap-GETcookie-env-0 {[string length $JR] > 0} |
| 319 | test_json_envelope_ok json-cap-GETcookie-env |
| 320 | test json-cap-GETcookie-name {[dict get $JR payload name] eq "anonymous"} |
| 321 | test json-cap-GETcookie-notsetup {![dict get $JR payload permissionFlags setup]} |
| 322 | |
| 323 | |
| 324 | # json cap via GET with authToken in a parameter |
| 325 | fossil_post_json "/json/cap?authToken=[dict get $AuthAnon authToken]" {} |
| 326 | test json-cap-GETcookie-env-0 {[string length $JR] > 0} |
| 327 | test_json_envelope_ok json-cap-GETcookie-env |
| 328 | test json-cap-GETcookie-name {[dict get $JR payload name] eq "anonymous"} |
| 329 | test json-cap-GETcookie-notsetup {![dict get $JR payload permissionFlags setup]} |
| 330 | |
| 331 | |
| 332 | # whoami |
| 333 | # via CLI with no auth token supplied |
| 334 | fossil_json whoami |
| @@ -675,27 +695,36 @@ | |
| 675 | # error happens before we have made the determination that the app is |
| 676 | # in JSON mode or if the error handling is incorrectly not |
| 677 | # recognizing JSON mode. |
| 678 | # |
| 679 | #test_setup x.fossil |
| 680 | #catch {exec chmod 444 .rep.fossil}; # Unix. What about Win? |
| 681 | fossil_http_json /json/timeline/checkin $U1Cookie |
| 682 | test json-ROrepo-1-1 {$CODE == 0} |
| 683 | test json-ROrepo-1-2 {[regexp {\}\s*$} $RESULT]} |
| 684 | test json-ROrepo-1-3 {![regexp {SQLITE_[A-Z]+:} $RESULT]} |
| 685 | test_json_envelope_ok json-http-timeline1 |
| 686 | protOut "chmod 444 repo" |
| 687 | catch {exec chmod 444 .rep.fossil}; # Unix |
| 688 | catch {exec attrib +r .rep.fossil}; # Windows |
| 689 | fossil_http_json /json/timeline/checkin $U1Cookie -expectError |
| 690 | test json-ROrepo-2-1 {$CODE != 0} |
| 691 | test json-ROrepo-2-2 {[regexp {\}\s*$} $RESULT]} knownBug |
| 692 | test json-ROrepo-2-3 {![regexp {SQLITE_[A-Z]+:} $RESULT]} knownBug |
| 693 | #test_json_envelope_ok json-http-timeline2 |
| 694 | catch {exec attrib -r .rep.fossil}; # Windows |
| 695 | catch {exec chmod 666 .rep.fossil}; # Unix |
| 696 | |
| 697 | |
| 698 | #### Result Codes |
| 699 | # Test cases designed to stimulate each (documented) error code. |
| 700 | |
| 701 | # FOSSIL-0000 |
| 702 |
| --- test/json.test | |
| +++ test/json.test | |
| @@ -33,11 +33,14 @@ | |
| 33 | # We need a JSON parser to effectively test the JSON produced by |
| 34 | # fossil. It looks like the one from tcllib is exactly what we need. |
| 35 | # On ActiveTcl, add it with teacup. On other platforms, YMMV. |
| 36 | # teacup install json |
| 37 | # teacup install json::write |
| 38 | if {[catch {package require json}] != 0} then { |
| 39 | puts "The \"json\" package is not available." |
| 40 | test_cleanup_then_return |
| 41 | } |
| 42 | |
| 43 | proc json2dict {txt} { |
| 44 | set rc [catch {::json::json2dict $txt} result options] |
| 45 | if {$rc != 0} { |
| 46 | protOut "JSON ERROR: $result" |
| @@ -74,11 +77,12 @@ | |
| 77 | # |
| 78 | # Returns the status code from the HTTP header. |
| 79 | proc fossil_http_json {url {cookie "Muppet=Monster"} args} { |
| 80 | global RESULT JR |
| 81 | set request "GET $url HTTP/1.1\r\nHost: localhost\r\nUser-Agent: Fossil-http-json\r\nCookie: $cookie" |
| 82 | set RESULT [fossil_maybe_answer $request http {*}$args --ipaddr 127.0.0.1] |
| 83 | set head ""; set body ""; set status "--NO_MATCH--" |
| 84 | regexp {(?w)(.*)^\s*$(.*)} $RESULT dummy head body |
| 85 | regexp {^HTTP\S+\s+(\d\d\d)\s+(.*)$} $head dummy status msg |
| 86 | if {$status eq "200"} { |
| 87 | set JR [json2dict $body] |
| 88 | } |
| @@ -115,13 +119,14 @@ | |
| 119 | } |
| 120 | |
| 121 | # handle the actual request |
| 122 | flush stdout |
| 123 | #exec $fossilexe |
| 124 | set RESULT [fossil_maybe_answer $request http {*}$args --ipaddr 127.0.0.1] |
| 125 | |
| 126 | # separate HTTP headers from body |
| 127 | set head ""; set body ""; set status "--NO_MATCH--" |
| 128 | regexp {(?w)(.*)^\s*$(.*)} $RESULT dummy head body |
| 129 | regexp {^HTTP\S+\s+(\d\d\d)\s+(.*)$} $head dummy status msg |
| 130 | if {$status eq "200"} { |
| 131 | if {[string length $body] > 0} { |
| 132 | set JR [json2dict $body] |
| @@ -170,10 +175,13 @@ | |
| 175 | |
| 176 | #### VERSION AKA HAI |
| 177 | |
| 178 | # The JSON API generally assumes we have a respository, so let it have one. |
| 179 | test_setup |
| 180 | |
| 181 | # Stop backoffice from running during this test as it can cause hangs. |
| 182 | fossil settings backoffice-disable 1 |
| 183 | |
| 184 | # Check for basic envelope fields in the result with an error |
| 185 | fossil_json -expectError |
| 186 | test_json_envelope json-enverr [concat resultCode fossil timestamp \ |
| 187 | resultText command procTimeUs procTimeMs] {} |
| @@ -307,28 +315,40 @@ | |
| 315 | # json cap via POST with authToken in request envelope |
| 316 | set anon2 [read_file anon-2] |
| 317 | fossil_post_json "/json/cap" $anon2 |
| 318 | test json-cap-POSTenv-env-0 {[string length $JR] > 0} |
| 319 | test_json_envelope_ok json-cap-POSTenv-env |
| 320 | if {[catch {test json-cap-POSTenv-name \ |
| 321 | {[dict get $JR payload name] eq "anonymous"} knownBug} jerr]} then { |
| 322 | test json-cap-POSTenv-name-threw 0 |
| 323 | protOut "CAUGHT: $jerr" |
| 324 | } |
| 325 | test json-cap-POSTenv-notsetup {![dict get $JR payload permissionFlags setup]} |
| 326 | |
| 327 | |
| 328 | # json cap via GET with authToken in Cookie header |
| 329 | fossil_post_json "/json/cap" {} $AnonCookie |
| 330 | test json-cap-GETcookie-env-0 {[string length $JR] > 0} |
| 331 | test_json_envelope_ok json-cap-GETcookie-env-0 |
| 332 | if {[catch {test json-cap-GETcookie-name-0 \ |
| 333 | {[dict get $JR payload name] eq "anonymous"}} jerr]} then { |
| 334 | test json-cap-GETcookie-name-0-threw 0 |
| 335 | protOut "CAUGHT: $jerr" |
| 336 | } |
| 337 | test json-cap-GETcookie-notsetup-0 {![dict get $JR payload permissionFlags setup]} |
| 338 | |
| 339 | |
| 340 | # json cap via GET with authToken in a parameter |
| 341 | fossil_post_json "/json/cap?authToken=[dict get $AuthAnon authToken]" {} |
| 342 | test json-cap-GETcookie-env-1 {[string length $JR] > 0} |
| 343 | test_json_envelope_ok json-cap-GETcookie-env-1 |
| 344 | if {[catch {test json-cap-GETcookie-name-1 \ |
| 345 | {[dict get $JR payload name] eq "anonymous"}} jerr]} then { |
| 346 | test json-cap-GETcookie-name-1-threw 0 |
| 347 | protOut "CAUGHT: $jerr" |
| 348 | } |
| 349 | test json-cap-GETcookie-notsetup-1 {![dict get $JR payload permissionFlags setup]} |
| 350 | |
| 351 | |
| 352 | # whoami |
| 353 | # via CLI with no auth token supplied |
| 354 | fossil_json whoami |
| @@ -675,27 +695,36 @@ | |
| 695 | # error happens before we have made the determination that the app is |
| 696 | # in JSON mode or if the error handling is incorrectly not |
| 697 | # recognizing JSON mode. |
| 698 | # |
| 699 | #test_setup x.fossil |
| 700 | fossil_http_json /json/query?sql=PRAGMA%20repository.journal_mode%3Dwal $U1Cookie |
| 701 | test json-ROrepo-1-1 {$CODE == 0} |
| 702 | test json-ROrepo-1-2 {[regexp {\}\s*$} $RESULT]} |
| 703 | test json-ROrepo-1-3 {![regexp {SQLITE_[A-Z]+:} $RESULT]} |
| 704 | test_json_envelope_ok json-http-timeline1 |
| 705 | if {$is_windows} then { |
| 706 | catch {exec attrib +r .rep.fossil}; # Windows |
| 707 | } else { |
| 708 | catch {exec chmod 444 .rep.fossil}; # Unix |
| 709 | } |
| 710 | protOut "chmod 444 repo" |
| 711 | fossil_http_json /json/query?sql=PRAGMA%20repository.journal_mode%3Ddelete $U1Cookie -expectError --json-preserve-rc |
| 712 | test json-ROrepo-2-1 {$CODE != 0} |
| 713 | test json-ROrepo-2-2 {[regexp {\}\s*$} $RESULT]} |
| 714 | test json-ROrepo-2-3 {![regexp {SQLITE_[A-Z]+:} $RESULT]} |
| 715 | #test_json_envelope_ok json-http-timeline2 |
| 716 | if {$is_windows} then { |
| 717 | catch {exec attrib -r .rep.fossil}; # Windows |
| 718 | catch {exec attrib -r .rep.fossil-shm} |
| 719 | catch {exec attrib -r .rep.fossil-wal} |
| 720 | } else { |
| 721 | catch {exec chmod 666 .rep.fossil}; # Unix |
| 722 | catch {exec chmod 666 .rep.fossil-shm} |
| 723 | catch {exec chmod 666 .rep.fossil-wal} |
| 724 | } |
| 725 | protOut "chmod 666 repo" |
| 726 | |
| 727 | #### Result Codes |
| 728 | # Test cases designed to stimulate each (documented) error code. |
| 729 | |
| 730 | # FOSSIL-0000 |
| 731 |
+38
-20
| --- test/tester.tcl | ||
| +++ test/tester.tcl | ||
| @@ -170,39 +170,57 @@ | ||
| 170 | 170 | set index [lsearch -exact $args -keepNewline] |
| 171 | 171 | if {$index != -1} { |
| 172 | 172 | set keepNewline 1 |
| 173 | 173 | set args [lreplace $args $index $index] |
| 174 | 174 | } |
| 175 | + set whatIf 0 | |
| 176 | + set index [lsearch -exact $args -whatIf] | |
| 177 | + if {$index != -1} { | |
| 178 | + set whatIf 1 | |
| 179 | + set args [lreplace $args $index $index] | |
| 180 | + } | |
| 175 | 181 | foreach a $args { |
| 176 | 182 | lappend cmd $a |
| 177 | 183 | } |
| 178 | 184 | protOut $cmd |
| 179 | 185 | |
| 180 | 186 | flush stdout |
| 181 | - if {[string length $answer] > 0} { | |
| 182 | - protOut $answer | |
| 183 | - set prompt_file [file join $::tempPath fossil_prompt_answer] | |
| 184 | - write_file $prompt_file $answer\n | |
| 185 | - if {$keepNewline} { | |
| 186 | - set rc [catch {eval exec -keepnewline $cmd <$prompt_file} result] | |
| 187 | - } else { | |
| 188 | - set rc [catch {eval exec $cmd <$prompt_file} result] | |
| 189 | - } | |
| 190 | - file delete $prompt_file | |
| 191 | - } else { | |
| 192 | - if {$keepNewline} { | |
| 193 | - set rc [catch {eval exec -keepnewline $cmd} result] | |
| 194 | - } else { | |
| 195 | - set rc [catch {eval exec $cmd} result] | |
| 196 | - } | |
| 187 | + if {$whatIf} { | |
| 188 | + protOut [pwd]; protOut $answer | |
| 189 | + set result WHAT-IF-MODE; set rc 42 | |
| 190 | + } else { | |
| 191 | + if {[string length $answer] > 0} { | |
| 192 | + protOut $answer | |
| 193 | + set prompt_file [file join $::tempPath fossil_prompt_answer] | |
| 194 | + write_file $prompt_file $answer\n | |
| 195 | + set execCmd [list eval exec] | |
| 196 | + if {$keepNewline} {lappend execCmd -keepnewline} | |
| 197 | + lappend execCmd $cmd <$prompt_file | |
| 198 | + set rc [catch $execCmd result] | |
| 199 | + file delete $prompt_file | |
| 200 | + } else { | |
| 201 | + set execCmd [list eval exec] | |
| 202 | + if {$keepNewline} {lappend execCmd -keepnewline} | |
| 203 | + lappend execCmd $cmd | |
| 204 | + set rc [catch $execCmd result] | |
| 205 | + } | |
| 206 | + } | |
| 207 | + set ab(str) {child process exited abnormally} | |
| 208 | + set ab(len) [string length $ab(str)] | |
| 209 | + set ab(off) [expr {$ab(len) - 1}] | |
| 210 | + if {$rc && $expectError && \ | |
| 211 | + [string range $result end-$ab(off) end] eq $ab(str)} { | |
| 212 | + set result [string range $result 0 end-$ab(len)] | |
| 197 | 213 | } |
| 198 | 214 | global RESULT CODE |
| 199 | 215 | set CODE $rc |
| 200 | - if {($rc && !$expectError) || (!$rc && $expectError)} { | |
| 201 | - protOut "ERROR: $result" 1 | |
| 202 | - } elseif {$::VERBOSE} { | |
| 203 | - protOut "RESULT: $result" | |
| 216 | + if {!$whatIf} { | |
| 217 | + if {($rc && !$expectError) || (!$rc && $expectError)} { | |
| 218 | + protOut "ERROR ($rc): $result" 1 | |
| 219 | + } elseif {$::VERBOSE} { | |
| 220 | + protOut "RESULT ($rc): $result" | |
| 221 | + } | |
| 204 | 222 | } |
| 205 | 223 | set RESULT $result |
| 206 | 224 | } |
| 207 | 225 | |
| 208 | 226 | # Read a file into memory. |
| 209 | 227 |
| --- test/tester.tcl | |
| +++ test/tester.tcl | |
| @@ -170,39 +170,57 @@ | |
| 170 | set index [lsearch -exact $args -keepNewline] |
| 171 | if {$index != -1} { |
| 172 | set keepNewline 1 |
| 173 | set args [lreplace $args $index $index] |
| 174 | } |
| 175 | foreach a $args { |
| 176 | lappend cmd $a |
| 177 | } |
| 178 | protOut $cmd |
| 179 | |
| 180 | flush stdout |
| 181 | if {[string length $answer] > 0} { |
| 182 | protOut $answer |
| 183 | set prompt_file [file join $::tempPath fossil_prompt_answer] |
| 184 | write_file $prompt_file $answer\n |
| 185 | if {$keepNewline} { |
| 186 | set rc [catch {eval exec -keepnewline $cmd <$prompt_file} result] |
| 187 | } else { |
| 188 | set rc [catch {eval exec $cmd <$prompt_file} result] |
| 189 | } |
| 190 | file delete $prompt_file |
| 191 | } else { |
| 192 | if {$keepNewline} { |
| 193 | set rc [catch {eval exec -keepnewline $cmd} result] |
| 194 | } else { |
| 195 | set rc [catch {eval exec $cmd} result] |
| 196 | } |
| 197 | } |
| 198 | global RESULT CODE |
| 199 | set CODE $rc |
| 200 | if {($rc && !$expectError) || (!$rc && $expectError)} { |
| 201 | protOut "ERROR: $result" 1 |
| 202 | } elseif {$::VERBOSE} { |
| 203 | protOut "RESULT: $result" |
| 204 | } |
| 205 | set RESULT $result |
| 206 | } |
| 207 | |
| 208 | # Read a file into memory. |
| 209 |
| --- test/tester.tcl | |
| +++ test/tester.tcl | |
| @@ -170,39 +170,57 @@ | |
| 170 | set index [lsearch -exact $args -keepNewline] |
| 171 | if {$index != -1} { |
| 172 | set keepNewline 1 |
| 173 | set args [lreplace $args $index $index] |
| 174 | } |
| 175 | set whatIf 0 |
| 176 | set index [lsearch -exact $args -whatIf] |
| 177 | if {$index != -1} { |
| 178 | set whatIf 1 |
| 179 | set args [lreplace $args $index $index] |
| 180 | } |
| 181 | foreach a $args { |
| 182 | lappend cmd $a |
| 183 | } |
| 184 | protOut $cmd |
| 185 | |
| 186 | flush stdout |
| 187 | if {$whatIf} { |
| 188 | protOut [pwd]; protOut $answer |
| 189 | set result WHAT-IF-MODE; set rc 42 |
| 190 | } else { |
| 191 | if {[string length $answer] > 0} { |
| 192 | protOut $answer |
| 193 | set prompt_file [file join $::tempPath fossil_prompt_answer] |
| 194 | write_file $prompt_file $answer\n |
| 195 | set execCmd [list eval exec] |
| 196 | if {$keepNewline} {lappend execCmd -keepnewline} |
| 197 | lappend execCmd $cmd <$prompt_file |
| 198 | set rc [catch $execCmd result] |
| 199 | file delete $prompt_file |
| 200 | } else { |
| 201 | set execCmd [list eval exec] |
| 202 | if {$keepNewline} {lappend execCmd -keepnewline} |
| 203 | lappend execCmd $cmd |
| 204 | set rc [catch $execCmd result] |
| 205 | } |
| 206 | } |
| 207 | set ab(str) {child process exited abnormally} |
| 208 | set ab(len) [string length $ab(str)] |
| 209 | set ab(off) [expr {$ab(len) - 1}] |
| 210 | if {$rc && $expectError && \ |
| 211 | [string range $result end-$ab(off) end] eq $ab(str)} { |
| 212 | set result [string range $result 0 end-$ab(len)] |
| 213 | } |
| 214 | global RESULT CODE |
| 215 | set CODE $rc |
| 216 | if {!$whatIf} { |
| 217 | if {($rc && !$expectError) || (!$rc && $expectError)} { |
| 218 | protOut "ERROR ($rc): $result" 1 |
| 219 | } elseif {$::VERBOSE} { |
| 220 | protOut "RESULT ($rc): $result" |
| 221 | } |
| 222 | } |
| 223 | set RESULT $result |
| 224 | } |
| 225 | |
| 226 | # Read a file into memory. |
| 227 |
+38
-20
| --- test/tester.tcl | ||
| +++ test/tester.tcl | ||
| @@ -170,39 +170,57 @@ | ||
| 170 | 170 | set index [lsearch -exact $args -keepNewline] |
| 171 | 171 | if {$index != -1} { |
| 172 | 172 | set keepNewline 1 |
| 173 | 173 | set args [lreplace $args $index $index] |
| 174 | 174 | } |
| 175 | + set whatIf 0 | |
| 176 | + set index [lsearch -exact $args -whatIf] | |
| 177 | + if {$index != -1} { | |
| 178 | + set whatIf 1 | |
| 179 | + set args [lreplace $args $index $index] | |
| 180 | + } | |
| 175 | 181 | foreach a $args { |
| 176 | 182 | lappend cmd $a |
| 177 | 183 | } |
| 178 | 184 | protOut $cmd |
| 179 | 185 | |
| 180 | 186 | flush stdout |
| 181 | - if {[string length $answer] > 0} { | |
| 182 | - protOut $answer | |
| 183 | - set prompt_file [file join $::tempPath fossil_prompt_answer] | |
| 184 | - write_file $prompt_file $answer\n | |
| 185 | - if {$keepNewline} { | |
| 186 | - set rc [catch {eval exec -keepnewline $cmd <$prompt_file} result] | |
| 187 | - } else { | |
| 188 | - set rc [catch {eval exec $cmd <$prompt_file} result] | |
| 189 | - } | |
| 190 | - file delete $prompt_file | |
| 191 | - } else { | |
| 192 | - if {$keepNewline} { | |
| 193 | - set rc [catch {eval exec -keepnewline $cmd} result] | |
| 194 | - } else { | |
| 195 | - set rc [catch {eval exec $cmd} result] | |
| 196 | - } | |
| 187 | + if {$whatIf} { | |
| 188 | + protOut [pwd]; protOut $answer | |
| 189 | + set result WHAT-IF-MODE; set rc 42 | |
| 190 | + } else { | |
| 191 | + if {[string length $answer] > 0} { | |
| 192 | + protOut $answer | |
| 193 | + set prompt_file [file join $::tempPath fossil_prompt_answer] | |
| 194 | + write_file $prompt_file $answer\n | |
| 195 | + set execCmd [list eval exec] | |
| 196 | + if {$keepNewline} {lappend execCmd -keepnewline} | |
| 197 | + lappend execCmd $cmd <$prompt_file | |
| 198 | + set rc [catch $execCmd result] | |
| 199 | + file delete $prompt_file | |
| 200 | + } else { | |
| 201 | + set execCmd [list eval exec] | |
| 202 | + if {$keepNewline} {lappend execCmd -keepnewline} | |
| 203 | + lappend execCmd $cmd | |
| 204 | + set rc [catch $execCmd result] | |
| 205 | + } | |
| 206 | + } | |
| 207 | + set ab(str) {child process exited abnormally} | |
| 208 | + set ab(len) [string length $ab(str)] | |
| 209 | + set ab(off) [expr {$ab(len) - 1}] | |
| 210 | + if {$rc && $expectError && \ | |
| 211 | + [string range $result end-$ab(off) end] eq $ab(str)} { | |
| 212 | + set result [string range $result 0 end-$ab(len)] | |
| 197 | 213 | } |
| 198 | 214 | global RESULT CODE |
| 199 | 215 | set CODE $rc |
| 200 | - if {($rc && !$expectError) || (!$rc && $expectError)} { | |
| 201 | - protOut "ERROR: $result" 1 | |
| 202 | - } elseif {$::VERBOSE} { | |
| 203 | - protOut "RESULT: $result" | |
| 216 | + if {!$whatIf} { | |
| 217 | + if {($rc && !$expectError) || (!$rc && $expectError)} { | |
| 218 | + protOut "ERROR ($rc): $result" 1 | |
| 219 | + } elseif {$::VERBOSE} { | |
| 220 | + protOut "RESULT ($rc): $result" | |
| 221 | + } | |
| 204 | 222 | } |
| 205 | 223 | set RESULT $result |
| 206 | 224 | } |
| 207 | 225 | |
| 208 | 226 | # Read a file into memory. |
| 209 | 227 |
| --- test/tester.tcl | |
| +++ test/tester.tcl | |
| @@ -170,39 +170,57 @@ | |
| 170 | set index [lsearch -exact $args -keepNewline] |
| 171 | if {$index != -1} { |
| 172 | set keepNewline 1 |
| 173 | set args [lreplace $args $index $index] |
| 174 | } |
| 175 | foreach a $args { |
| 176 | lappend cmd $a |
| 177 | } |
| 178 | protOut $cmd |
| 179 | |
| 180 | flush stdout |
| 181 | if {[string length $answer] > 0} { |
| 182 | protOut $answer |
| 183 | set prompt_file [file join $::tempPath fossil_prompt_answer] |
| 184 | write_file $prompt_file $answer\n |
| 185 | if {$keepNewline} { |
| 186 | set rc [catch {eval exec -keepnewline $cmd <$prompt_file} result] |
| 187 | } else { |
| 188 | set rc [catch {eval exec $cmd <$prompt_file} result] |
| 189 | } |
| 190 | file delete $prompt_file |
| 191 | } else { |
| 192 | if {$keepNewline} { |
| 193 | set rc [catch {eval exec -keepnewline $cmd} result] |
| 194 | } else { |
| 195 | set rc [catch {eval exec $cmd} result] |
| 196 | } |
| 197 | } |
| 198 | global RESULT CODE |
| 199 | set CODE $rc |
| 200 | if {($rc && !$expectError) || (!$rc && $expectError)} { |
| 201 | protOut "ERROR: $result" 1 |
| 202 | } elseif {$::VERBOSE} { |
| 203 | protOut "RESULT: $result" |
| 204 | } |
| 205 | set RESULT $result |
| 206 | } |
| 207 | |
| 208 | # Read a file into memory. |
| 209 |
| --- test/tester.tcl | |
| +++ test/tester.tcl | |
| @@ -170,39 +170,57 @@ | |
| 170 | set index [lsearch -exact $args -keepNewline] |
| 171 | if {$index != -1} { |
| 172 | set keepNewline 1 |
| 173 | set args [lreplace $args $index $index] |
| 174 | } |
| 175 | set whatIf 0 |
| 176 | set index [lsearch -exact $args -whatIf] |
| 177 | if {$index != -1} { |
| 178 | set whatIf 1 |
| 179 | set args [lreplace $args $index $index] |
| 180 | } |
| 181 | foreach a $args { |
| 182 | lappend cmd $a |
| 183 | } |
| 184 | protOut $cmd |
| 185 | |
| 186 | flush stdout |
| 187 | if {$whatIf} { |
| 188 | protOut [pwd]; protOut $answer |
| 189 | set result WHAT-IF-MODE; set rc 42 |
| 190 | } else { |
| 191 | if {[string length $answer] > 0} { |
| 192 | protOut $answer |
| 193 | set prompt_file [file join $::tempPath fossil_prompt_answer] |
| 194 | write_file $prompt_file $answer\n |
| 195 | set execCmd [list eval exec] |
| 196 | if {$keepNewline} {lappend execCmd -keepnewline} |
| 197 | lappend execCmd $cmd <$prompt_file |
| 198 | set rc [catch $execCmd result] |
| 199 | file delete $prompt_file |
| 200 | } else { |
| 201 | set execCmd [list eval exec] |
| 202 | if {$keepNewline} {lappend execCmd -keepnewline} |
| 203 | lappend execCmd $cmd |
| 204 | set rc [catch $execCmd result] |
| 205 | } |
| 206 | } |
| 207 | set ab(str) {child process exited abnormally} |
| 208 | set ab(len) [string length $ab(str)] |
| 209 | set ab(off) [expr {$ab(len) - 1}] |
| 210 | if {$rc && $expectError && \ |
| 211 | [string range $result end-$ab(off) end] eq $ab(str)} { |
| 212 | set result [string range $result 0 end-$ab(len)] |
| 213 | } |
| 214 | global RESULT CODE |
| 215 | set CODE $rc |
| 216 | if {!$whatIf} { |
| 217 | if {($rc && !$expectError) || (!$rc && $expectError)} { |
| 218 | protOut "ERROR ($rc): $result" 1 |
| 219 | } elseif {$::VERBOSE} { |
| 220 | protOut "RESULT ($rc): $result" |
| 221 | } |
| 222 | } |
| 223 | set RESULT $result |
| 224 | } |
| 225 | |
| 226 | # Read a file into memory. |
| 227 |