Skip to content

Commit

Permalink
Improve couch_js_tests
Browse files Browse the repository at this point in the history
There are a few minor improvements:

- Add more tests to check sandboxing resets, and that docs are "frozen".

- Remove the extra `\n` and `"` around function body lines. Erlang can do
  multi-line binaries just fine. Mark the sections with %erlfmt-ignore to the
  formatter doesn't complain.

- Generalize `should_create_sandbox` test to check for the `not defined` string
  only. Experimenting with QuickJS noticed that it uses single quotes
  around`'Object.foo' is not defined` and SM doesn't. So check for `not
  defined` part only as it's obvious enough what the check is about.

- Make sure to return test procs back to the pool. Previously, none of the
  tests returned the processes back into the pool, and when the tests ended,
  they were forcibly killed which resulted in log noise that looked like:

```
erl_child_setup: failed with error 32 on line 265
erl_child_setup: failed with error 32 on line 265
...
```
  • Loading branch information
nickva committed Mar 16, 2023
1 parent 0aedd93 commit b976247
Showing 1 changed file with 111 additions and 60 deletions.
171 changes: 111 additions & 60 deletions src/couch/test/eunit/couch_js_tests.erl
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ couch_js_test_() ->
fun test_util:stop_couch/1,
[
fun should_create_sandbox/0,
fun should_reset_properly/0,
fun should_freeze_doc_object/0,
fun should_roundtrip_utf8/0,
fun should_roundtrip_modified_utf8/0,
fun should_replace_broken_utf16/0,
Expand All @@ -31,72 +33,117 @@ couch_js_test_() ->
}
}.

%% erlfmt-ignore
should_create_sandbox() ->
% Try and detect whether we can see out of the
% sandbox or not.
Src = <<
"function(doc) {\n"
" try {\n"
" emit(false, typeof(Couch.compile_function));\n"
" } catch (e) {\n"
" emit(true, e.message);\n"
" }\n"
"}\n"
>>,
Src = <<"
function(doc) {
try {
emit(false, typeof(Couch.compile_function));
} catch (e) {
emit(true, e.message);
}
}
">>,
Proc = couch_query_servers:get_os_process(<<"javascript">>),
true = couch_query_servers:proc_prompt(Proc, [<<"add_fun">>, Src]),
Result = couch_query_servers:proc_prompt(Proc, [<<"map_doc">>, {[]}]),
?assertMatch([[[true, <<_/binary>>]]], Result),
[[[true, ErrMsg]]] = Result,
?assertNotEqual([], binary:matches(ErrMsg, <<"not defined">>)),
couch_query_servers:ret_os_process(Proc).

%% erlfmt-ignore
should_reset_properly() ->
Src = <<"
function(doc) {
var a = [0,1,2];
emit(a.indexOf(0), Object.foo);
Object.foo = 43;
[].constructor.prototype.indexOf = function(x) {return 42;};
}
">>,
Proc = couch_query_servers:get_os_process(<<"javascript">>),
true = couch_query_servers:proc_prompt(Proc, [<<"add_fun">>, Src]),
Result = couch_query_servers:proc_prompt(Proc, [<<"map_doc">>, <<"{}">>]),
?assertEqual([[[true, <<"Couch is not defined">>]]], Result).
Doc = {[]},
Result1 = couch_query_servers:proc_prompt(Proc, [<<"map_doc">>, Doc]),
?assertEqual([[[0, null]]], Result1),
Result2 = couch_query_servers:proc_prompt(Proc, [<<"map_doc">>, Doc]),
?assertEqual([[[42, 43]]], Result2),
true = couch_query_servers:proc_prompt(Proc, [<<"reset">>]),
true = couch_query_servers:proc_prompt(Proc, [<<"add_fun">>, Src]),
Result3 = couch_query_servers:proc_prompt(Proc, [<<"map_doc">>, Doc]),
?assertEqual([[[0, null]]], Result3),
couch_query_servers:ret_os_process(Proc).

%% erlfmt-ignore
should_freeze_doc_object() ->
Src = <<"
function(doc) {
emit(doc.foo, doc.bar);
doc.foo = 1042;
doc.bar = 1043;
emit(doc.foo, doc.bar);
}
">>,
Proc = couch_query_servers:get_os_process(<<"javascript">>),
true = couch_query_servers:proc_prompt(Proc, [<<"add_fun">>, Src]),
Doc = {[{<<"bar">>, 1041}]},
Result1 = couch_query_servers:proc_prompt(Proc, [<<"map_doc">>, Doc]),
?assertEqual([[[null, 1041], [null, 1041]]], Result1),
Result2 = couch_query_servers:proc_prompt(Proc, [<<"map_doc">>, Doc]),
?assertEqual([[[null, 1041], [null, 1041]]], Result2),
couch_query_servers:ret_os_process(Proc).

%% erlfmt-ignore
should_roundtrip_utf8() ->
% Try round tripping UTF-8 both directions through
% couchjs. These tests use hex encoded values of
% Ä (C384) and Ü (C39C) so as to avoid odd editor/Erlang encoding
% strangeness.
Src = <<
"function(doc) {\n"
" emit(doc.value, \"",
16#C3,
16#9C,
"\");\n"
"}\n"
>>,
"function(doc) {
emit(doc.value, \"", 16#C3, 16#9C, "\");
}
">>,
Proc = couch_query_servers:get_os_process(<<"javascript">>),
true = couch_query_servers:proc_prompt(Proc, [<<"add_fun">>, Src]),
Doc =
{[
{<<"value">>, <<16#C3, 16#84>>}
]},
Result = couch_query_servers:proc_prompt(Proc, [<<"map_doc">>, Doc]),
?assertEqual([[[<<16#C3, 16#84>>, <<16#C3, 16#9C>>]]], Result).
?assertEqual([[[<<16#C3, 16#84>>, <<16#C3, 16#9C>>]]], Result),
couch_query_servers:ret_os_process(Proc).

%% erlfmt-ignore
should_roundtrip_modified_utf8() ->
% Mimicking the test case from the mailing list
Src = <<
"function(doc) {\n"
" emit(doc.value.toLowerCase(), \"",
16#C3,
16#9C,
"\");\n"
"}\n"
>>,
Src = <<"
function(doc) {
emit(doc.value.toLowerCase(), \"", 16#C3, 16#9C, "\");
}
">>,
Proc = couch_query_servers:get_os_process(<<"javascript">>),
true = couch_query_servers:proc_prompt(Proc, [<<"add_fun">>, Src]),
Doc =
{[
{<<"value">>, <<16#C3, 16#84>>}
]},
Result = couch_query_servers:proc_prompt(Proc, [<<"map_doc">>, Doc]),
?assertEqual([[[<<16#C3, 16#A4>>, <<16#C3, 16#9C>>]]], Result).
?assertEqual([[[<<16#C3, 16#A4>>, <<16#C3, 16#9C>>]]], Result),
couch_query_servers:ret_os_process(Proc).

%% erlfmt-ignore
should_replace_broken_utf16() ->
% This test reverse the surrogate pair of
% the Boom emoji U+1F4A5
Src = <<
"function(doc) {\n"
" emit(doc.value.split(\"\").reverse().join(\"\"), 1);\n"
"}\n"
>>,
Src = <<"
function(doc) {
emit(doc.value.split(\"\").reverse().join(\"\"), 1);
}
">>,
Proc = couch_query_servers:get_os_process(<<"javascript">>),
true = couch_query_servers:proc_prompt(Proc, [<<"add_fun">>, Src]),
Doc =
Expand All @@ -107,8 +154,10 @@ should_replace_broken_utf16() ->
% Invalid UTF-8 gets replaced with the 16#FFFD replacement
% marker
Markers = list_to_binary(xmerl_ucs:to_utf8([16#FFFD, 16#FFFD])),
?assertEqual([[[Markers, 1]]], Result).
?assertEqual([[[Markers, 1]]], Result),
couch_query_servers:ret_os_process(Proc).

%% erlfmt-ignore
should_allow_js_string_mutations() ->
% This binary corresponds to this string: мама мыла раму
% Which I'm told translates to: "mom was washing the frame"
Expand Down Expand Up @@ -142,21 +191,21 @@ should_allow_js_string_mutations() ->
>>,
Mom = <<16#D0, 16#BC, 16#D0, 16#B0, 16#D0, 16#BC, 16#D0, 16#B0>>,
Washed = <<16#D0, 16#BC, 16#D1, 16#8B, 16#D0, 16#BB, 16#D0, 16#B0>>,
Src1 = <<
"function(doc) {\n"
" emit(\"length\", doc.value.length);\n"
"}\n"
>>,
Src2 = <<
"function(doc) {\n"
" emit(\"substring\", doc.value.substring(5, 9));\n"
"}\n"
>>,
Src3 = <<
"function(doc) {\n"
" emit(\"slice\", doc.value.slice(0, 4));\n"
"}\n"
>>,
Src1 = <<"
function(doc) {
emit(\"length\", doc.value.length);
}
">>,
Src2 = <<"
function(doc) {
emit(\"substring\", doc.value.substring(5, 9));
}
">>,
Src3 = <<"
function(doc) {
emit(\"slice\", doc.value.slice(0, 4));
}
">>,
Proc = couch_query_servers:get_os_process(<<"javascript">>),
true = couch_query_servers:proc_prompt(Proc, [<<"add_fun">>, Src1]),
true = couch_query_servers:proc_prompt(Proc, [<<"add_fun">>, Src2]),
Expand All @@ -168,19 +217,21 @@ should_allow_js_string_mutations() ->
[[<<"substring">>, Washed]],
[[<<"slice">>, Mom]]
],
?assertEqual(Expect, Result).
?assertEqual(Expect, Result),
couch_query_servers:ret_os_process(Proc).

%% erlfmt-ignore
should_exit_on_oom() ->
Src = <<
"var state = [];\n"
"function(doc) {\n"
" var val = \"0123456789ABCDEF\";\n"
" for(var i = 0; i < 665535; i++) {\n"
" state.push([val, val]);\n"
" emit(null, null);\n"
" }\n"
"}\n"
>>,
Src = <<"
var state = [];
function(doc) {
var val = \"0123456789ABCDEF\";
for(var i = 0; i < 665535; i++) {
state.push([val, val]);
emit(null, null);
}
}
">>,
Proc = couch_query_servers:get_os_process(<<"javascript">>),
true = couch_query_servers:proc_prompt(Proc, [<<"add_fun">>, Src]),
trigger_oom(Proc).
Expand Down

0 comments on commit b976247

Please sign in to comment.