Browse Source

Move the final old HTTP suite tests and remove it

Loïc Hoguin 6 years ago
parent
commit
0223f69fcd

+ 23 - 0
test/handlers/accept_callback_h.erl

@@ -0,0 +1,23 @@
+%% This module returns something different in
+%% AcceptCallback depending on the query string.
+
+-module(accept_callback_h).
+
+-export([init/2]).
+-export([allowed_methods/2]).
+-export([content_types_accepted/2]).
+-export([put_text_plain/2]).
+
+init(Req, Opts) ->
+	{cowboy_rest, Req, Opts}.
+
+allowed_methods(Req, State) ->
+	{[<<"PUT">>, <<"POST">>, <<"PATCH">>], Req, State}.
+
+content_types_accepted(Req, State) ->
+	{[{{<<"text">>, <<"plain">>, []}, put_text_plain}], Req, State}.
+
+put_text_plain(Req=#{qs := <<"false">>}, State) ->
+	{false, Req, State};
+put_text_plain(Req=#{qs := <<"true">>}, State) ->
+	{true, Req, State}.

+ 4 - 2
test/handlers/content_types_accepted_h.erl

@@ -1,5 +1,5 @@
-%% This module accepts a multipart media type with parameters
-%% that do not include boundary.
+%% This module returns something different in
+%% content_types_accepted depending on the query string.
 
 -module(content_types_accepted_h).
 
@@ -19,6 +19,8 @@ content_types_accepted(Req=#{qs := <<"multipart">>}, State) ->
 	{[
 		{{<<"multipart">>, <<"mixed">>, [{<<"v">>, <<"1">>}]}, put_multipart_mixed}
 	], Req, State};
+content_types_accepted(Req=#{qs := <<"param">>}, State) ->
+	{[{{<<"text">>, <<"plain">>, [{<<"charset">>, <<"utf-8">>}]}, put_text_plain}], Req, State};
 content_types_accepted(Req=#{qs := <<"wildcard-param">>}, State) ->
 	{[{{<<"text">>, <<"plain">>, '*'}, put_text_plain}], Req, State}.
 

+ 17 - 0
test/handlers/delete_resource_h.erl

@@ -0,0 +1,17 @@
+%% This module accepts a multipart media type with parameters
+%% that do not include boundary.
+
+-module(delete_resource_h).
+
+-export([init/2]).
+-export([allowed_methods/2]).
+-export([delete_resource/2]).
+
+init(Req, Opts) ->
+	{cowboy_rest, Req, Opts}.
+
+allowed_methods(Req, State) ->
+	{[<<"DELETE">>], Req, State}.
+
+delete_resource(#{qs := <<"missing">>}, _) ->
+	no_call.

+ 39 - 0
test/handlers/generate_etag_h.erl

@@ -0,0 +1,39 @@
+%% This module sends a different etag value
+%% depending on the query string.
+
+-module(generate_etag_h).
+
+-export([init/2]).
+-export([content_types_provided/2]).
+-export([get_text_plain/2]).
+-export([generate_etag/2]).
+
+init(Req, Opts) ->
+	{cowboy_rest, Req, Opts}.
+
+content_types_provided(Req, State) ->
+	{[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}.
+
+get_text_plain(Req, State) ->
+	{<<"This is REST!">>, Req, State}.
+
+%% Correct return values from generate_etag/2.
+generate_etag(Req=#{qs := <<"tuple-weak">>}, State) ->
+	{{weak, <<"etag-header-value">>}, Req, State};
+generate_etag(Req=#{qs := <<"tuple-strong">>}, State) ->
+	{{strong, <<"etag-header-value">>}, Req, State};
+%% Backwards compatible return values from generate_etag/2.
+generate_etag(Req=#{qs := <<"binary-weak-quoted">>}, State) ->
+	{<<"W/\"etag-header-value\"">>, Req, State};
+generate_etag(Req=#{qs := <<"binary-strong-quoted">>}, State) ->
+	{<<"\"etag-header-value\"">>, Req, State};
+%% Invalid return values from generate_etag/2.
+generate_etag(Req=#{qs := <<"binary-weak-unquoted">>}, State) ->
+	ct_helper_error_h:ignore(cow_http_hd, parse_etag, 1),
+	{<<"W/etag-header-value">>, Req, State};
+generate_etag(Req=#{qs := <<"binary-strong-unquoted">>}, State) ->
+	ct_helper_error_h:ignore(cow_http_hd, parse_etag, 1),
+	{<<"etag-header-value">>, Req, State};
+%% Simulate the callback being missing in other cases.
+generate_etag(#{qs := <<"missing">>}, _) ->
+	no_call.

+ 0 - 183
test/old_http_SUITE.erl

@@ -1,183 +0,0 @@
-%% Copyright (c) 2011-2017, Loïc Hoguin <essen@ninenines.eu>
-%% Copyright (c) 2011, Anthony Ramine <nox@dev-extend.eu>
-%%
-%% Permission to use, copy, modify, and/or distribute this software for any
-%% purpose with or without fee is hereby granted, provided that the above
-%% copyright notice and this permission notice appear in all copies.
-%%
-%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
--module(old_http_SUITE).
--compile(export_all).
--compile(nowarn_export_all).
-
--import(ct_helper, [config/2]).
--import(cowboy_test, [gun_open/1]).
--import(cowboy_test, [gun_open/2]).
--import(cowboy_test, [gun_down/1]).
--import(cowboy_test, [raw_open/1]).
--import(cowboy_test, [raw_send/2]).
--import(cowboy_test, [raw_recv_head/1]).
--import(cowboy_test, [raw_expect_recv/2]).
-
-%% ct.
-
-all() ->
-	[
-		{group, http},
-		{group, https},
-		{group, http_compress},
-		{group, https_compress}
-	].
-
-groups() ->
-	Tests = ct_helper:all(?MODULE),
-	[
-		{http, [], Tests}, %% @todo parallel
-		{https, [parallel], Tests},
-		{http_compress, [parallel], Tests},
-		{https_compress, [parallel], Tests}
-	].
-
-init_per_group(Name = http, Config) ->
-	cowboy_test:init_http(Name, #{env => #{dispatch => init_dispatch(Config)}}, Config);
-init_per_group(Name = https, Config) ->
-	cowboy_test:init_https(Name, #{env => #{dispatch => init_dispatch(Config)}}, Config);
-init_per_group(Name = http_compress, Config) ->
-	cowboy_test:init_http(Name, #{
-		env => #{dispatch => init_dispatch(Config)},
-		stream_handlers => [cowboy_compress_h, cowboy_stream_h]
-	}, Config);
-init_per_group(Name = https_compress, Config) ->
-	cowboy_test:init_https(Name, #{
-		env => #{dispatch => init_dispatch(Config)},
-		stream_handlers => [cowboy_compress_h, cowboy_stream_h]
-	}, Config).
-
-end_per_group(Name, _) ->
-	ok = cowboy:stop_listener(Name).
-
-%% Dispatch configuration.
-
-init_dispatch(_) ->
-	cowboy_router:compile([
-		{"localhost", [
-			{"/chunked_response", http_chunked, []},
-			{"/headers/dupe", http_handler,
-				[{headers, #{<<"connection">> => <<"close">>}}]},
-			{"/set_resp/header", http_set_resp,
-				[{headers, #{<<"vary">> => <<"Accept">>}}]},
-			{"/set_resp/overwrite", http_set_resp,
-				[{headers, #{<<"server">> => <<"DesireDrive/1.0">>}}]},
-			{"/set_resp/body", http_set_resp,
-				[{body, <<"A flameless dance does not equal a cycle">>}]},
-			{"/handler_errors", http_errors, []},
-			{"/echo/body", http_echo_body, []},
-			{"/param_all", rest_param_all, []},
-			{"/bad_accept", rest_simple_resource, []},
-			{"/bad_content_type", rest_patch_resource, []},
-			{"/simple", rest_simple_resource, []},
-			{"/forbidden_post", rest_forbidden_resource, [true]},
-			{"/simple_post", rest_forbidden_resource, [false]},
-			{"/missing_get_callbacks", rest_missing_callbacks, []},
-			{"/missing_put_callbacks", rest_missing_callbacks, []},
-			{"/nodelete", rest_nodelete_resource, []},
-			{"/post_charset", rest_post_charset_resource, []},
-			{"/postonly", rest_postonly_resource, []},
-			{"/patch", rest_patch_resource, []},
-			{"/resetags", rest_resource_etags, []},
-			{"/rest_expires", rest_expires, []},
-			{"/rest_expires_binary", rest_expires_binary, []},
-			{"/rest_empty_resource", rest_empty_resource, []},
-			{"/loop_stream_recv", http_loop_stream_recv, []},
-			{"/", http_handler, []}
-		]}
-	]).
-
-%% Tests.
-
-rest_bad_content_type(Config) ->
-	ConnPid = gun_open(Config),
-	Ref = gun:patch(ConnPid, "/bad_content_type",
-		[{<<"content-type">>, <<"text/plain, text/html">>}], <<"Whatever">>),
-	{response, fin, 415, _} = gun:await(ConnPid, Ref),
-	ok.
-
-rest_nodelete(Config) ->
-	ConnPid = gun_open(Config),
-	Ref = gun:delete(ConnPid, "/nodelete"),
-	{response, fin, 500, _} = gun:await(ConnPid, Ref),
-	ok.
-
-rest_patch(Config) ->
-	Tests = [
-		{204, [{<<"content-type">>, <<"text/plain">>}], <<"whatever">>},
-		{400, [{<<"content-type">>, <<"text/plain">>}], <<"false">>},
-		{400, [{<<"content-type">>, <<"text/plain">>}], <<"stop">>},
-		{415, [{<<"content-type">>, <<"application/json">>}], <<"bad_content_type">>}
-	],
-	ConnPid = gun_open(Config),
-	_ = [begin
-		Ref = gun:patch(ConnPid, "/patch", Headers, Body),
-		{response, fin, Status, _} = gun:await(ConnPid, Ref)
-	end || {Status, Headers, Body} <- Tests],
-	ok.
-
-rest_post_charset(Config) ->
-	ConnPid = gun_open(Config),
-	Ref = gun:post(ConnPid, "/post_charset",
-		[{<<"content-type">>, <<"text/plain;charset=UTF-8">>}], "12345"),
-	{response, fin, 204, _} = gun:await(ConnPid, Ref),
-	ok.
-
-rest_postonly(Config) ->
-	ConnPid = gun_open(Config),
-	Ref = gun:post(ConnPid, "/postonly",
-		[{<<"content-type">>, <<"text/plain">>}], "12345"),
-	{response, fin, 204, _} = gun:await(ConnPid, Ref),
-	ok.
-
-rest_resource_get_etag(Config, Type) ->
-	rest_resource_get_etag(Config, Type, []).
-
-rest_resource_get_etag(Config, Type, Headers) ->
-	ConnPid = gun_open(Config),
-	Ref = gun:get(ConnPid, "/resetags?type=" ++ Type, Headers),
-	{response, _, Status, RespHeaders} = gun:await(ConnPid, Ref),
-	case lists:keyfind(<<"etag">>, 1, RespHeaders) of
-		false -> {Status, false};
-		{<<"etag">>, ETag} -> {Status, ETag}
-	end.
-
-rest_resource_etags(Config) ->
-	Tests = [
-		{200, <<"W/\"etag-header-value\"">>, "tuple-weak"},
-		{200, <<"\"etag-header-value\"">>, "tuple-strong"},
-		{200, <<"W/\"etag-header-value\"">>, "binary-weak-quoted"},
-		{200, <<"\"etag-header-value\"">>, "binary-strong-quoted"},
-		{500, false, "binary-strong-unquoted"},
-		{500, false, "binary-weak-unquoted"}
-	],
-	_ = [{Status, ETag, Type} = begin
-		{Ret, RespETag} = rest_resource_get_etag(Config, Type),
-		{Ret, RespETag, Type}
-	end || {Status, ETag, Type} <- Tests].
-
-rest_resource_etags_if_none_match(Config) ->
-	Tests = [
-		{304, <<"W/\"etag-header-value\"">>, "tuple-weak"},
-		{304, <<"\"etag-header-value\"">>, "tuple-strong"},
-		{304, <<"W/\"etag-header-value\"">>, "binary-weak-quoted"},
-		{304, <<"\"etag-header-value\"">>, "binary-strong-quoted"}
-	],
-	_ = [{Status, Type} = begin
-		{Ret, _} = rest_resource_get_etag(Config, Type,
-			[{<<"if-none-match">>, ETag}]),
-		{Ret, Type}
-	end || {Status, ETag, Type} <- Tests].

+ 0 - 15
test/old_http_SUITE_data/http_chunked.erl

@@ -1,15 +0,0 @@
-%% Feel free to use, reuse and abuse the code in this file.
-
--module(http_chunked).
-
--export([init/2]).
-
-init(Req, Opts) ->
-	Req2 = cowboy_req:stream_reply(200, Req),
-	%% Try an empty chunk to make sure the stream doesn't get closed.
-	cowboy_req:stream_body([<<>>], nofin, Req2),
-	timer:sleep(100),
-	cowboy_req:stream_body("chunked_handler\r\n", nofin, Req2),
-	timer:sleep(100),
-	cowboy_req:stream_body("works fine!", fin, Req2),
-	{ok, Req2, Opts}.

+ 0 - 21
test/old_http_SUITE_data/http_echo_body.erl

@@ -1,21 +0,0 @@
-%% Feel free to use, reuse and abuse the code in this file.
-
--module(http_echo_body).
-
--export([init/2]).
-
-init(Req, Opts) ->
-	true = cowboy_req:has_body(Req),
-	Req3 = case cowboy_req:read_body(Req, #{length => 1000000}) of
-		{ok, Body, Req2} -> handle_body(Req2, Body);
-		{more, _, Req2} -> handle_badlength(Req2)
-	end,
-	{ok, Req3, Opts}.
-
-handle_badlength(Req) ->
-	cowboy_req:reply(413, #{}, <<"Request entity too large">>, Req).
-
-handle_body(Req, Body) ->
-	Size = cowboy_req:body_length(Req),
-	Size = byte_size(Body),
-	cowboy_req:reply(200, #{}, Body, Req).

+ 0 - 19
test/old_http_SUITE_data/http_errors.erl

@@ -1,19 +0,0 @@
-%% Feel free to use, reuse and abuse the code in this file.
-
--module(http_errors).
-
--export([init/2]).
-
--spec init(_, _) -> no_return().
-init(Req, _Opts) ->
-	#{'case' := Case} = cowboy_req:match_qs(['case'], Req),
-    case_init(Case, Req).
-
--spec case_init(_, _) -> no_return().
-case_init(<<"init_before_reply">> = Case, _Req) ->
-	ct_helper_error_h:ignore(?MODULE, case_init, 2),
-    error(Case);
-case_init(<<"init_after_reply">> = Case, Req) ->
-	ct_helper_error_h:ignore(?MODULE, case_init, 2),
-    _ = cowboy_req:reply(200, #{}, "http_handler_crashes", Req),
-    error(Case).

+ 0 - 10
test/old_http_SUITE_data/http_handler.erl

@@ -1,10 +0,0 @@
-%% Feel free to use, reuse and abuse the code in this file.
-
--module(http_handler).
-
--export([init/2]).
-
-init(Req, Opts) ->
-	Headers = proplists:get_value(headers, Opts, #{}),
-	Body = proplists:get_value(body, Opts, "http_handler"),
-	{ok, cowboy_req:reply(200, Headers, Body, Req), Opts}.

+ 0 - 34
test/old_http_SUITE_data/http_loop_stream_recv.erl

@@ -1,34 +0,0 @@
-%% Feel free to use, reuse and abuse the code in this file.
-
--module(http_loop_stream_recv).
-
--export([init/2]).
--export([info/3]).
--export([terminate/3]).
-
-init(Req, _) ->
-	receive after 100 -> ok end,
-	self() ! stream,
-	{cowboy_loop, Req, undefined}.
-
-info(stream, Req, undefined) ->
-	stream(Req, 1, <<>>).
-
-stream(Req, ID, Acc) ->
-	case cowboy_req:read_body(Req) of
-		{ok, <<>>, Req2} ->
-			{stop, cowboy_req:reply(200, Req2), undefined};
-		{_, Data, Req2} ->
-			parse_id(Req2, ID, << Acc/binary, Data/binary >>)
-	end.
-
-parse_id(Req, ID, Data) ->
-	case Data of
-		<< ID:32, Rest/bits >> ->
-			parse_id(Req, ID + 1, Rest);
-		_ ->
-			stream(Req, ID, Data)
-	end.
-
-terminate(stop, _, _) ->
-	ok.

+ 0 - 25
test/old_http_SUITE_data/http_set_resp.erl

@@ -1,25 +0,0 @@
-%% Feel free to use, reuse and abuse the code in this file.
-
--module(http_set_resp).
-
--export([init/2]).
-
-init(Req, Opts) ->
-	Headers = proplists:get_value(headers, Opts, #{}),
-	Body = proplists:get_value(body, Opts, <<"http_handler_set_resp">>),
-	Req2 = lists:foldl(fun({Name, Value}, R) ->
-		cowboy_req:set_resp_header(Name, Value, R)
-	end, Req, maps:to_list(Headers)),
-	Req3 = cowboy_req:set_resp_body(Body, Req2),
-	Req4 = cowboy_req:set_resp_header(<<"x-cowboy-test">>, <<"ok">>, Req3),
-	Req5 = cowboy_req:set_resp_cookie(<<"cake">>, <<"lie">>, Req4),
-	case cowboy_req:has_resp_header(<<"x-cowboy-test">>, Req5) of
-		false -> {ok, Req5, Opts};
-		true ->
-			case cowboy_req:has_resp_body(Req5) of
-				false ->
-					{ok, Req5, Opts};
-				true ->
-					{ok, cowboy_req:reply(200, Req5), Opts}
-			end
-	end.

+ 0 - 6
test/old_http_SUITE_data/rest_empty_resource.erl

@@ -1,6 +0,0 @@
--module(rest_empty_resource).
-
--export([init/2]).
-
-init(Req, Opts) ->
-	{cowboy_rest, Req, Opts}.

+ 0 - 22
test/old_http_SUITE_data/rest_expires.erl

@@ -1,22 +0,0 @@
--module(rest_expires).
-
--export([init/2]).
--export([content_types_provided/2]).
--export([get_text_plain/2]).
--export([expires/2]).
--export([last_modified/2]).
-
-init(Req, Opts) ->
-	{cowboy_rest, Req, Opts}.
-
-content_types_provided(Req, State) ->
-	{[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}.
-
-get_text_plain(Req, State) ->
-	{<<"This is REST!">>, Req, State}.
-
-expires(Req, State) ->
-	{{{2012, 9, 21}, {22, 36, 14}}, Req, State}.
-
-last_modified(Req, State) ->
-	{{{2012, 9, 21}, {22, 36, 14}}, Req, State}.

+ 0 - 18
test/old_http_SUITE_data/rest_expires_binary.erl

@@ -1,18 +0,0 @@
--module(rest_expires_binary).
-
--export([init/2]).
--export([content_types_provided/2]).
--export([get_text_plain/2]).
--export([expires/2]).
-
-init(Req, Opts) ->
-	{cowboy_rest, Req, Opts}.
-
-content_types_provided(Req, State) ->
-	{[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}.
-
-get_text_plain(Req, State) ->
-	{<<"This is REST!">>, Req, State}.
-
-expires(Req, State) ->
-	{<<"0">>, Req, State}.

+ 0 - 32
test/old_http_SUITE_data/rest_forbidden_resource.erl

@@ -1,32 +0,0 @@
--module(rest_forbidden_resource).
-
--export([init/2]).
--export([allowed_methods/2]).
--export([forbidden/2]).
--export([content_types_provided/2]).
--export([content_types_accepted/2]).
--export([to_text/2]).
--export([from_text/2]).
-
-init(Req, [Forbidden]) ->
-	{cowboy_rest, Req, Forbidden}.
-
-allowed_methods(Req, State) ->
-	{[<<"GET">>, <<"HEAD">>, <<"POST">>], Req, State}.
-
-forbidden(Req, State=true) ->
-	{true, Req, State};
-forbidden(Req, State=false) ->
-	{false, Req, State}.
-
-content_types_provided(Req, State) ->
-	{[{{<<"text">>, <<"plain">>, []}, to_text}], Req, State}.
-
-content_types_accepted(Req, State) ->
-	{[{{<<"text">>, <<"plain">>, []}, from_text}], Req, State}.
-
-to_text(Req, State) ->
-	{<<"This is REST!">>, Req, State}.
-
-from_text(Req, State) ->
-	{{true, cowboy_req:path(Req)}, Req, State}.

+ 0 - 24
test/old_http_SUITE_data/rest_missing_callbacks.erl

@@ -1,24 +0,0 @@
--module(rest_missing_callbacks).
-
--export([init/2]).
--export([allowed_methods/2]).
--export([content_types_accepted/2]).
--export([content_types_provided/2]).
-
-init(Req, Opts) ->
-	{cowboy_rest, Req, Opts}.
-
-allowed_methods(Req, State) ->
-	{[<<"GET">>, <<"PUT">>], Req, State}.
-
-content_types_accepted(Req, State) ->
-	ct_helper_error_h:ignore(cowboy_rest, process_content_type, 3),
-	{[
-		{<<"application/json">>, put_application_json}
-	], Req, State}.
-
-content_types_provided(Req, State) ->
-	ct_helper_error_h:ignore(cowboy_rest, set_resp_body, 2),
-	{[
-		{<<"text/plain">>, get_text_plain}
-	], Req, State}.

+ 0 - 18
test/old_http_SUITE_data/rest_nodelete_resource.erl

@@ -1,18 +0,0 @@
--module(rest_nodelete_resource).
-
--export([init/2]).
--export([allowed_methods/2]).
--export([content_types_provided/2]).
--export([get_text_plain/2]).
-
-init(Req, Opts) ->
-	{cowboy_rest, Req, Opts}.
-
-allowed_methods(Req, State) ->
-	{[<<"GET">>, <<"HEAD">>, <<"DELETE">>], Req, State}.
-
-content_types_provided(Req, State) ->
-	{[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}.
-
-get_text_plain(Req, State) ->
-	{<<"This is REST!">>, Req, State}.

+ 0 - 36
test/old_http_SUITE_data/rest_param_all.erl

@@ -1,36 +0,0 @@
--module(rest_param_all).
-
--export([init/2]).
--export([allowed_methods/2]).
--export([content_types_provided/2]).
--export([get_text_plain/2]).
--export([content_types_accepted/2]).
--export([put_text_plain/2]).
-
-init(Req, Opts) ->
-	{cowboy_rest, Req, Opts}.
-
-allowed_methods(Req, State) ->
-	{[<<"GET">>, <<"PUT">>], Req, State}.
-
-content_types_provided(Req, State) ->
-	{[{{<<"text">>, <<"plain">>, '*'}, get_text_plain}], Req, State}.
-
-get_text_plain(Req, State) ->
-	{_, _, Param} = maps:get(media_type, Req, {<<"text">>, <<"plain">>, []}),
-	Body = if
-		Param == '*' ->
-			<<"'*'">>;
-		Param == [] ->
-			<<"[]">>;
-		Param /= [] ->
-			iolist_to_binary([[Key, $=, Value] || {Key, Value} <- Param])
-	end,
-	{Body, Req, State}.
-
-content_types_accepted(Req, State) ->
-	{[{{<<"text">>, <<"plain">>, '*'}, put_text_plain}], Req, State}.
-
-put_text_plain(Req0, State) ->
-	{ok, _, Req} = cowboy_req:read_body(Req0),
-	{true, Req, State}.

+ 0 - 38
test/old_http_SUITE_data/rest_patch_resource.erl

@@ -1,38 +0,0 @@
--module(rest_patch_resource).
-
--export([init/2]).
--export([allowed_methods/2]).
--export([content_types_provided/2]).
--export([get_text_plain/2]).
--export([content_types_accepted/2]).
--export([patch_text_plain/2]).
-
-init(Req, Opts) ->
-	{cowboy_rest, Req, Opts}.
-
-allowed_methods(Req, State) ->
-	{[<<"HEAD">>, <<"GET">>, <<"PATCH">>], Req, State}.
-
-content_types_provided(Req, State) ->
-	{[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}.
-
-get_text_plain(Req, State) ->
-	{<<"This is REST!">>, Req, State}.
-
-content_types_accepted(Req, State) ->
-	case cowboy_req:method(Req) of
-		<<"PATCH">> ->
-			{[{{<<"text">>, <<"plain">>, []}, patch_text_plain}], Req, State};
-		_ ->
-			{[], Req, State}
-	end.
-
-patch_text_plain(Req, State) ->
-	case cowboy_req:read_body(Req) of
-		{ok, <<"stop">>, Req0} ->
-			{stop, cowboy_req:reply(400, Req0), State};
-		{ok, <<"false">>, Req0} ->
-			{false, Req0, State};
-		{ok, _Body, Req0} ->
-			{true, Req0, State}
-	end.

+ 0 - 19
test/old_http_SUITE_data/rest_post_charset_resource.erl

@@ -1,19 +0,0 @@
--module(rest_post_charset_resource).
-
--export([init/2]).
--export([allowed_methods/2]).
--export([content_types_accepted/2]).
--export([from_text/2]).
-
-init(Req, Opts) ->
-	{cowboy_rest, Req, Opts}.
-
-allowed_methods(Req, State) ->
-	{[<<"POST">>], Req, State}.
-
-content_types_accepted(Req, State) ->
-	{[{{<<"text">>, <<"plain">>, [{<<"charset">>, <<"utf-8">>}]},
-		from_text}], Req, State}.
-
-from_text(Req, State) ->
-	{true, Req, State}.

+ 0 - 18
test/old_http_SUITE_data/rest_postonly_resource.erl

@@ -1,18 +0,0 @@
--module(rest_postonly_resource).
-
--export([init/2]).
--export([allowed_methods/2]).
--export([content_types_accepted/2]).
--export([from_text/2]).
-
-init(Req, Opts) ->
-	{cowboy_rest, Req, Opts}.
-
-allowed_methods(Req, State) ->
-	{[<<"POST">>], Req, State}.
-
-content_types_accepted(Req, State) ->
-	{[{{<<"text">>, <<"plain">>, '*'}, from_text}], Req, State}.
-
-from_text(Req, State) ->
-	{true, Req, State}.

+ 0 - 37
test/old_http_SUITE_data/rest_resource_etags.erl

@@ -1,37 +0,0 @@
--module(rest_resource_etags).
-
--export([init/2]).
--export([generate_etag/2]).
--export([content_types_provided/2]).
--export([get_text_plain/2]).
-
-init(Req, Opts) ->
-	{cowboy_rest, Req, Opts}.
-
-generate_etag(Req, State) ->
-	#{type := Type} = cowboy_req:match_qs([type], Req),
-	case Type of
-		%% Correct return values from generate_etag/2.
-		<<"tuple-weak">> ->
-			{{weak, <<"etag-header-value">>}, Req, State};
-		<<"tuple-strong">> ->
-			{{strong, <<"etag-header-value">>}, Req, State};
-		%% Backwards compatible return values from generate_etag/2.
-		<<"binary-weak-quoted">> ->
-			{<<"W/\"etag-header-value\"">>, Req, State};
-		<<"binary-strong-quoted">> ->
-			{<<"\"etag-header-value\"">>, Req, State};
-		%% Invalid return values from generate_etag/2.
-		<<"binary-strong-unquoted">> ->
-			ct_helper_error_h:ignore(cow_http_hd, parse_etag, 1),
-			{<<"etag-header-value">>, Req, State};
-		<<"binary-weak-unquoted">> ->
-			ct_helper_error_h:ignore(cow_http_hd, parse_etag, 1),
-			{<<"W/etag-header-value">>, Req, State}
-	end.
-
-content_types_provided(Req, State) ->
-	{[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}.
-
-get_text_plain(Req, State) ->
-	{<<"This is REST!">>, Req, State}.

+ 0 - 14
test/old_http_SUITE_data/rest_simple_resource.erl

@@ -1,14 +0,0 @@
--module(rest_simple_resource).
-
--export([init/2]).
--export([content_types_provided/2]).
--export([get_text_plain/2]).
-
-init(Req, Opts) ->
-	{cowboy_rest, Req, Opts}.
-
-content_types_provided(Req, State) ->
-	{[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}.
-
-get_text_plain(Req, State) ->
-	{<<"This is REST!">>, Req, State}.

+ 158 - 0
test/rest_handler_SUITE.erl

@@ -39,6 +39,7 @@ end_per_group(Name, _) ->
 init_dispatch(_) ->
 	cowboy_router:compile([{'_', [
 		{"/", rest_hello_h, []},
+		{"/accept_callback", accept_callback_h, []},
 		{"/accept_callback_missing", accept_callback_missing_h, []},
 		{"/charsets_provided", charsets_provided_h, []},
 		{"/charsets_provided_empty", charsets_provided_empty_h, []},
@@ -50,7 +51,9 @@ init_dispatch(_) ->
 			charset_in_content_types_provided_implicit_no_callback_h, []},
 		{"/content_types_accepted", content_types_accepted_h, []},
 		{"/content_types_provided", content_types_provided_h, []},
+		{"/delete_resource", delete_resource_h, []},
 		{"/expires", expires_h, []},
+		{"/generate_etag", generate_etag_h, []},
 		{"/if_range", if_range_h, []},
 		{"/last_modified", last_modified_h, []},
 		{"/provide_callback_missing", provide_callback_missing_h, []},
@@ -84,6 +87,44 @@ accept_callback_missing(Config) ->
 	{response, fin, 500, _} = gun:await(ConnPid, Ref),
 	ok.
 
+accept_callback_patch_false(Config) ->
+	do_accept_callback_false(Config, patch).
+
+accept_callback_patch_true(Config) ->
+	do_accept_callback_true(Config, patch).
+
+accept_callback_post_false(Config) ->
+	do_accept_callback_false(Config, post).
+
+accept_callback_post_true(Config) ->
+	do_accept_callback_true(Config, post).
+
+accept_callback_put_false(Config) ->
+	do_accept_callback_false(Config, put).
+
+accept_callback_put_true(Config) ->
+	do_accept_callback_true(Config, put).
+
+do_accept_callback_false(Config, Fun) ->
+	doc("When AcceptCallback returns false a 400 response must be returned."),
+	ConnPid = gun_open(Config),
+	Ref = gun:Fun(ConnPid, "/accept_callback?false", [
+		{<<"accept-encoding">>, <<"gzip">>},
+		{<<"content-type">>, <<"text/plain">>}
+	], <<"Request body.">>),
+	{response, _, 400, _} = gun:await(ConnPid, Ref),
+	ok.
+
+do_accept_callback_true(Config, Fun) ->
+	doc("When AcceptCallback returns true a 204 response must be returned."),
+	ConnPid = gun_open(Config),
+	Ref = gun:Fun(ConnPid, "/accept_callback?true", [
+		{<<"accept-encoding">>, <<"gzip">>},
+		{<<"content-type">>, <<"text/plain">>}
+	], <<"Request body.">>),
+	{response, _, 204, _} = gun:await(ConnPid, Ref),
+	ok.
+
 charset_in_content_types_provided(Config) ->
 	doc("When a charset is matched explictly in content_types_provided, "
 		"that charset is used and the charsets_provided callback is ignored."),
@@ -282,6 +323,17 @@ charsets_provided_empty_noheader(Config) ->
 	{response, _, 406, _} = gun:await(ConnPid, Ref),
 	ok.
 
+content_type_invalid(Config) ->
+	doc("An invalid content-type in a POST/PATCH/PUT request "
+		"must be rejected with a 415 unsupported media type response. (RFC7231 6.5.13)"),
+	ConnPid = gun_open(Config),
+	Ref = gun:put(ConnPid, "/content_types_accepted?wildcard-param", [
+		{<<"accept-encoding">>, <<"gzip">>},
+		{<<"content-type">>, <<"text/plain, text/html">>}
+	]),
+	{response, fin, 415, _} = gun:await(ConnPid, Ref),
+	ok.
+
 content_types_accepted_ignore_multipart_boundary(Config) ->
 	doc("When a multipart content-type is provided for the request "
 		"body, the boundary parameter is not expected to be returned "
@@ -295,6 +347,18 @@ content_types_accepted_ignore_multipart_boundary(Config) ->
 	{response, _, 204, _} = gun:await(ConnPid, Ref),
 	ok.
 
+content_types_accepted_param(Config) ->
+	doc("When a parameter is returned from the content_types_accepted "
+		"callback, and the same parameter is found in the content-type "
+		"header, the negotiation succeeds and the request is processed."),
+	ConnPid = gun_open(Config),
+	Ref = gun:put(ConnPid, "/content_types_accepted?param", [
+		{<<"accept-encoding">>, <<"gzip">>},
+		{<<"content-type">>, <<"text/plain;charset=UTF-8">>}
+	], "12345"),
+	{response, fin, 204, _} = gun:await(ConnPid, Ref),
+	ok.
+
 content_types_accepted_wildcard_param_no_content_type_param(Config) ->
 	doc("When a wildcard is returned for parameters from the "
 		"content_types_accepted callback, a content-type header "
@@ -381,6 +445,17 @@ content_types_provided_wildcard_param_no_accept_header(Config) ->
 	{ok, <<"[]">>} = gun:await_body(ConnPid, Ref),
 	ok.
 
+delete_resource_missing(Config) ->
+	doc("When a resource accepts the DELETE method and the "
+		"delete_resource callback is not exported, the "
+		"resource is incorrect and a 500 response is expected."),
+	ConnPid = gun_open(Config),
+	Ref = gun:delete(ConnPid, "/delete_resource?missing", [
+		{<<"accept-encoding">>, <<"gzip">>}
+	]),
+	{response, _, 500, _} = gun:await(ConnPid, Ref),
+	ok.
+
 error_on_malformed_accept(Config) ->
 	doc("A malformed Accept header must result in a 400 response."),
 	do_error_on_malformed_header(Config, <<"accept">>).
@@ -443,6 +518,89 @@ expires_undefined(Config) ->
 	false = lists:keyfind(<<"expires">>, 1, Headers),
 	ok.
 
+generate_etag_missing(Config) ->
+	doc("The etag header must not be sent when "
+		"the generate_etag callback is not exported."),
+	ConnPid = gun_open(Config),
+	Ref = gun:get(ConnPid, "/generate_etag?missing", [
+		{<<"accept-encoding">>, <<"gzip">>}
+	]),
+	{response, _, 200, Headers} = gun:await(ConnPid, Ref),
+	false = lists:keyfind(<<"etag">>, 1, Headers),
+	ok.
+
+generate_etag_binary_strong(Config) ->
+	doc("The etag header must be sent when the generate_etag "
+		"callback returns a strong binary. (RFC7232 2.3)"),
+	do_generate_etag(Config, "binary-strong-quoted",
+		[], 200, {<<"etag">>, <<"\"etag-header-value\"">>}).
+
+generate_etag_binary_weak(Config) ->
+	doc("The etag header must be sent when the generate_etag "
+		"callback returns a weak binary. (RFC7232 2.3)"),
+	do_generate_etag(Config, "binary-weak-quoted",
+		[], 200, {<<"etag">>, <<"W/\"etag-header-value\"">>}).
+
+generate_etag_invalid_binary_strong_unquoted(Config) ->
+	doc("When Cowboy cannot parse the generate_etag callback's "
+		"return value, a 500 response is returned without the etag header."),
+	do_generate_etag(Config, "binary-strong-unquoted", [], 500, false).
+
+generate_etag_invalid_binary_weak_unquoted(Config) ->
+	doc("When Cowboy cannot parse the generate_etag callback's "
+		"return value, a 500 response is returned without the etag header."),
+	do_generate_etag(Config, "binary-weak-unquoted", [], 500, false).
+
+generate_etag_tuple_strong(Config) ->
+	doc("The etag header must be sent when the generate_etag "
+		"callback returns a strong tuple. (RFC7232 2.3)"),
+	do_generate_etag(Config, "tuple-strong",
+		[], 200, {<<"etag">>, <<"\"etag-header-value\"">>}).
+
+generate_etag_tuple_weak(Config) ->
+	doc("The etag header must be sent when the generate_etag "
+		"callback returns a weak tuple. (RFC7232 2.3)"),
+	do_generate_etag(Config, "tuple-weak",
+		[], 200, {<<"etag">>, <<"W/\"etag-header-value\"">>}).
+
+if_none_match_binary_strong(Config) ->
+	doc("When the if-none-match header matches a strong etag, "
+		"a 304 not modified response is returned. (RFC7232 3.2)"),
+	do_generate_etag(Config, "binary-strong-quoted",
+		[{<<"if-none-match">>, <<"\"etag-header-value\"">>}],
+		304, {<<"etag">>, <<"\"etag-header-value\"">>}).
+
+if_none_match_binary_weak(Config) ->
+	doc("When the if-none-match header matches a weak etag, "
+		"a 304 not modified response is returned. (RFC7232 3.2)"),
+	do_generate_etag(Config, "binary-weak-quoted",
+		[{<<"if-none-match">>, <<"W/\"etag-header-value\"">>}],
+		304, {<<"etag">>, <<"W/\"etag-header-value\"">>}).
+
+if_none_match_tuple_strong(Config) ->
+	doc("When the if-none-match header matches a strong etag, "
+		"a 304 not modified response is returned. (RFC7232 3.2)"),
+	do_generate_etag(Config, "tuple-strong",
+		[{<<"if-none-match">>, <<"\"etag-header-value\"">>}],
+		304, {<<"etag">>, <<"\"etag-header-value\"">>}).
+
+if_none_match_tuple_weak(Config) ->
+	doc("When the if-none-match header matches a weak etag, "
+		"a 304 not modified response is returned. (RFC7232 3.2)"),
+	do_generate_etag(Config, "tuple-weak",
+		[{<<"if-none-match">>, <<"W/\"etag-header-value\"">>}],
+		304, {<<"etag">>, <<"W/\"etag-header-value\"">>}).
+
+do_generate_etag(Config, Qs, ReqHeaders, Status, Etag) ->
+	ConnPid = gun_open(Config),
+	Ref = gun:get(ConnPid, "/generate_etag?" ++ Qs, [
+		{<<"accept-encoding">>, <<"gzip">>}
+		|ReqHeaders
+	]),
+	{response, _, Status, RespHeaders} = gun:await(ConnPid, Ref),
+	Etag = lists:keyfind(<<"etag">>, 1, RespHeaders),
+	ok.
+
 if_range_etag_equal(Config) ->
 	doc("When the if-range header matches, a 206 partial content "
 		"response is expected for an otherwise valid range request. (RFC7233 3.2)"),