How can I unit test Nancy modules with F#? - unit-testing

I'm trying to test Nancy modules with F# as described here, the thing is I can't see how to pass the second parameter in F#.
Here's what I have so far:
let should_return_status_ok_for_get() =
let bootstrapper = new DefaultNancyBootstrapper()
let browser = new Browser(bootstrapper, fun req -> req.Accept(new Responses.Negotiation.MediaRange("application/json")))
let result = browser.Get("/Menu", fun req -> req.HttpRequest())
Assert.AreEqual (HttpStatusCode.OK, result.StatusCode)
result
in the example, I should be able to instantiate a Browser object to test a specific Module:
var browser = new Browser(with => with.Module(new MySimpleModule()));
But I get a compile time error in F# when I try:
let browser = new Browser(fun req -> req.Module(new MenuModule()))
EDIT Error: No overloads match for method 'Browser'
Are there any examples of this in F#?
Also, is this the best way to go about this in F#?

This is how I run Nancy tests in F#:
I create a new bootstrapper in my test project by deriving from the DefaultNancyBootstrapper. I use this bootstrapper to register my mocks:
type Bootstrapper() =
inherit DefaultNancyBootstrapper()
override this.ConfigureApplicationContainer(container : TinyIoCContainer) =
base.ConfigureApplicationContainer(container)
container.Register<IMyClass, MyMockClass>() |> ignore
Then I write a simple test method to execute a GET request like so:
[<TestFixture>]
type ``Health Check Tests`` () =
[<Test>]
member test.``Given the service is healthy the health check endpoint returns a HTTP 200 response with status message "Everything is OK"`` () =
let bootstrapper = new Bootstrapper()
let browser = new Browser(bootstrapper)
let result = browser.Get("/healthcheck")
let healthCheckResponse = JsonSerializer.deserialize<HealthCheckResponse> <| result.Body.AsString()
result.StatusCode |> should equal HttpStatusCode.OK
healthCheckResponse.Message |> should equal "Everything is OK"
Let me know if this helps!

Related

actix web test doesn't seem to be routing requests as expected

I recently updated to actix web 4, I had some tests that used the actix-web test module that stopped working as expected in the process. I'm sure it's something simple but I'm having trouble figuring out what changed. Here is a minimal example of the issue:
use actix_web::{test, web, App, HttpResponse, HttpRequest};
#[actix_rt::test]
async fn says_hello() {
let req = test::TestRequest::get().uri("/index.html").to_request();
let mut server =
test::init_service(App::new().service(web::scope("/").route("index.html", web::get().to(|_req: HttpRequest| async {
println!("Hello?");
HttpResponse::Ok()
})))).await;
let _resp = test::call_and_read_body(&mut server, req).await;
}
running this test I would expect to see "Hello?" output to my console, however, the request handler function I have defined at "/index.html" doesn't seem to be called and I receive no output.
To be clear, the tests are more complicated and have assertions etc, this is just a working example of the main issue I am trying to resolve
actix-web = { version = "4.1.0", default-features = false }
note:
if I change all paths to the root path it will call the handler, I.E.
let req = test::TestRequest::get().uri("/").to_request();
let mut server =
test::init_service(App::new().service(web::scope("/").route("/", web::get().to(|_req: HttpRequest| async {
println!("Hello?");
HttpResponse::Ok()
})))).await;
let _resp = test::call_and_read_body(&mut server, req).await;
// prints "Hello?" to the console
However no other route combination I have tried calls the request handler.
Rust tests capture the output and only output them for failed tests.
If you want to show output on all tests you have to tell them to do so with either testbinary --nocapture or cargo test -- --nocapture.
I was able to make things work by changing the path in the scope to an empty string
let req = test::TestRequest::get().uri("/index.html").to_request();
let mut server =
test::init_service(App::new().service(web::scope("").route("index.html", web::get().to(|_req: HttpRequest| async {
println!("Hello?");
HttpResponse::Ok()
})))).await;
let _resp = test::call_and_read_body(&mut server, req).await;
// prints "Hello?"

MirageOS - Http-fetch example

I'm trying to modify a bit the MirageOS http-fetch example (https://github.com/mirage/mirage-skeleton) that can be found inside mirage-skeleton but I'm having some problems understanding why I can't move some of the function executed inside the config.ml file to my unikernel.ml file. The original config.ml file follows (I'll copy just the interesting part) :
[...]
let client =
foreign "Unikernel.Client" ## console #-> resolver #-> conduit #-> job
let () =
add_to_ocamlfind_libraries ["mirage-http"];
add_to_opam_packages ["mirage-http"];
let sv4 = stack default_console in
let res_dns = resolver_dns sv4 in
let conduit = conduit_direct sv4 in
let job = [ client $ default_console $ res_dns $ conduit ] in
register "http-fetch" job
What I'm trying to do is move these two lines :
let res_dns = resolver_dns sv4 in
let conduit = conduit_direct sv4 in
into my unikernel.ml start method. Basically I want to pass to my module just the stack and let it create a dns resolver and a conduit. My start function follows:
let start c s =
C.log_s c (sprintf "Resolving in 1s using DNS server %s" ns) >>= fun () ->
OS.Time.sleep 1.0 >>= fun () ->
let res_dns = resolver_dns s in
let conduit = conduit_direct s in
http_fetch c res_dns conduit >>= fun (data) ->
Lwt.return(dump_to_db data);
Right now I'm getting this error at http_fetch parameters submission:
Error: This expression has type Mirage.resolver Mirage.impl
but an expression was expected of type Resolver_lwt.t
What I'm asking here is mostly a conceptual question because I'm clearly missing something. I'm not an expert in OCaml/MirageOS but this controversial behaviour of type mismatch is hard to understand considering that I'm just calling the same function from a different file.
config.ml is used to generate main.ml. You can copy the generated code from there if you want.

Can't launch ocsigen server due to failure : ("That function cannot be called here because it needs information about the request or the site.")

I want to create a service who generates its HTML according to the parameter given and a map. Given the parameter, the service search in the map for the html, and a function to launch on client side.
type sample =
(string (* little text *)*
Html5_types.html Eliom_content.Html5.elt (* html page *) *
(unit -> unit)(* Demonstration function *))
Given that the function is to be launched on client side, I insert it in the map as a client value :
{client{
let demo_function = ignore (Ojquery.add_html
(Ojquery.jQ "li") "<p id='test1'>new paragraph</p>") }}
let get_samples () =
let samples_map = Samples.empty in
let samples_map = Samples.add "add_html"
("text",
(Eliom_tools.F.html
(** html stuff **)
),
{unit->unit{demo_function}}) samples_map in
samples_map
And then I register the service like this :
let sample_service =
Eliom_service.service
~path:["examples"]
~get_params:Eliom_parameter.(string "entry")
()
let () =
Examples_app.register
~service:sample_service
(fun (entry) () ->
try
(let entry = Samples.find entry samples_map in
let html = ((function (name, html, func) -> html) entry) in
let func = ((function (name, html, func) -> func) entry) in
ignore {unit{%func ()}};
Lwt.return (html))
with Not_found -> Lwt.return (not_found)
)
The rest of the code is pretty much only the result of a classic eliom-distillery, with the inclusion of the ojquery package for the client function used.
The compilation phase goes smoothly, but when I try to launch the server, I get the following error message :
ocsigenserver: main: Fatal - Error in configuration file: Error while parsing configuration file: Eliom: while loading local/lib/examples/examples.cma: Failure("That function cannot be called here because it needs information about the request or the site.")
My first guess was that it is due to the fact that I store client values outside of a service, but is there any way to store this kind of values on the server?
I tried to wrap them in regular functions :
let demo_serv_func () = {unit{demo_client_func ()}}
But the problem remained...
I found the issue. The problem was not because I stored client functions, but because I used Eliom_tools.F.html outside of a service.
It happens that Eliom_tools needs the context of the service to function, and since I was storing it outside of the service, it could not work.
I solved the issue by using Eliom_tools inside the service, and storing the body of the HTML page in the map.

Handling Cookies in Ocamlnet

I'm trying to write a bot pulling some data which is only available to authenticated users. I settled for ocaml (v. 3.12.1) and ocamlnet (v. 3.6.5). The first part of the script sends a POST request to the website and by the html I receive back, I can tell that the authentication worked (p1 and p2's values in this code sample are obviously not the ones I'm using).
open Http_client
open Nethttp
let pipeline = new pipeline
let () =
let post_call = new post
"http://www.kraland.org/main.php?p=1&a=100"
[("p1", "username");
("p2", "password");
("Submit", "Ok!")]
in
pipeline#add post_call;
pipeline#run();
Then I extract the cookies where the php session id, the account name, a hash of the password, etc. are stored, put them in the header of the next request and run it. And this is where I run into troubles: I systematically get the boring page every anonymous visitor gets.
let cookies = Header.get_set_cookie post_call#response_header in
let get_call = new get "http://www.kraland.org/main.php?p=1" in
let header = get_call#request_header `Base in
Header.set_set_cookie header cookies;
pipeline#add get_call;
pipeline#run();
When I print the content of the cookies, I do get something weird: I would expect the domain of the cookies to be kraland.org but it does not seem to be the case. This is the printing command I use together with the output:
List.iter (fun c -> Printf.printf "%.0f [%s%s:%b] %s := %s\n"
(match c.cookie_expires with None -> -1. | Some f -> f)
(match c.cookie_domain with None -> "" | Some s -> s)
(match c.cookie_path with None -> "" | Some s -> s)
c.cookie_secure c.cookie_name c.cookie_value)
cookies;
-1 [/:false] PHPSESSID := 410b97b0536b3e949df17edd44965926
1372719625 [:false] login := username
1372719625 [:false] id := myid
1372719625 [:false] password := fbCK/0M+blFRLx3oDp+24bHlwpDUy7x885sF+Q865ms=
1372719625 [:false] pc_id := 872176495311
Edit: I had a go at the problem using Haskell's Http-conduit-browser and it works like a charm using something very much like the doc's example.

AngularJS - basic testing with injection

So I'm new to the whole testing thing (I've been one of those people who has said 'I should write unit tests...' but never ended up ever doing it :-p).
I'm now writing unit tests for this project.  I'm using testacular + Jasmine, with browserify to compile things.  I was having no problems until I started trying to do a lot AngularJS injection-stuff.
Right now I'm simply trying to do a test of ng-model to get my head around all of it.
I have a testacular.conf file which includes everything necessary:
files = [
'../lib/jquery.js',
'../lib/angular.js',
'./lib/jasmine.js',
'./lib/angular-mocks.js',
JASMINE_ADAPTER,
'./tests.js' //compiled by browserify
];
I have my controller defined (MainCtrl.coffee)
MainCtrl = ($scope, $rootScope) ->
$scope.hello = 'initial'
module.exports = (angularModule) ->
angularModule.controller 'MainCtrl', ['$scope', '$rootScope', MainCtrl]
return MainCtrl
And I have my test itself: (_MainCtrlTest.coffee, in same directory as MainCtrl.coffee)
testModule = angular.module 'MainCtrlTest', []
MainCtrl = require('./MainCtrl')(testModule)
describe 'MainCtrlTest', ->
scope = null
elm = null
ctrl = null
beforeEach inject ($rootScope, $compile, $controller) ->
scope = $rootScope.$new()
ctrl = $controller MainCtrl, $scope: scope
elm = $compile('<input ng-model="hello"/>')(scope)
describe 'value $scope.hello', ->
it 'should initially equal input value', ->
expect(elm.val()).toBe scope.hello
it 'should change when input value changes', ->
scope.$apply -> elm.val('changedValue')
expect(scope.hello).toBe elm.val()
The test fails immediately, with the input's elm.val() returning blank, and scope.hello returning the intended value ('initial', set in MainCtrl.coffee)
What am I doing wrong here?
To get this working, you need to do scope.$apply():
it 'should initially equal input value', ->
scope.$apply()
expect(elm.val()).toBe scope.hello
Don't test the framework, test your code
Your test is trying to test whether Angular's binding, and ng-model works. You should rather trust the framework and test your code instead.
Your code is:
the controller (setting initial scope.hello value)
html templates (and all the binding, directives in there)
You can test the first one very easily, without even touching any DOM. That's the beauty of AngularJS - strong separation of view/logic.
In this controller, there is almost nothing to test, but the initial value:
it 'should init hello', ->
expect(scope.hello).toBe 'initial'
To test the second one (template + binding), you want to do an e2e test. You basically want to test, whether the template doesn't contain any typos in binding etc... So you wanna test the real template. If you inline a different html during the test, you are testing nothing but AngularJS.