/*
 * Copyright (C) 2023 Hasl Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#include <glib.h>

#include <hasl.h>

/******************************************************************************
 * Helpers
 *****************************************************************************/
static void
test_hasl_mechanism_anonymous_error_test(HaslContext *context,
                                         gint error_code)
{
	HaslMechanism *mechanism = NULL;
	HaslMechanismResult result = 0;
	GError *error = NULL;
	guint8 *client_out = NULL;
	gsize client_out_length = 0;

	mechanism = g_object_new(HASL_TYPE_MECHANISM_ANONYMOUS, NULL);

	result = hasl_mechanism_step(mechanism, context, NULL, 0, &client_out,
	                             &client_out_length, &error);

	g_assert_error(error, HASL_MECHANISM_ANONYMOUS_ERROR, error_code);
	g_clear_error(&error);

	g_assert_cmpint(result, ==, HASL_MECHANISM_RESULT_ERROR);

	g_assert_null(client_out);
	g_assert_cmpint(client_out_length, ==, 0);

	g_clear_object(&mechanism);
}

static void
test_hasl_mechanism_anonymous_possible(HaslContext *context, gboolean expected,
                                       gboolean should_error, gint error_code)
{
	HaslMechanism *mechanism = NULL;
	GError *error = NULL;
	gboolean ret = FALSE;

	mechanism = g_object_new(HASL_TYPE_MECHANISM_ANONYMOUS, NULL);

	ret = hasl_mechanism_possible(mechanism, context, &error);

	if(should_error) {
		g_assert_error(error, HASL_MECHANISM_ANONYMOUS_ERROR, error_code);
		g_clear_error(&error);
	} else {
		g_assert_no_error(error);
	}

	g_assert_true(ret == expected);

	g_clear_object(&mechanism);
}

/******************************************************************************
 * Tests
 *****************************************************************************/
static void
test_hasl_mechanism_anonymous_new(void) {
	HaslMechanism *mechanism = NULL;

	mechanism = g_object_new(HASL_TYPE_MECHANISM_ANONYMOUS, NULL);

	g_assert_true(HASL_IS_MECHANISM_ANONYMOUS(mechanism));
	g_assert_true(HASL_IS_MECHANISM(mechanism));

	g_clear_object(&mechanism);
}

static void
test_hasl_mechanism_anonymous_username_required_null(void) {
	HaslContext *context = NULL;

	context = hasl_context_new();
	hasl_context_set_password(context, "hunter2");

	test_hasl_mechanism_anonymous_error_test(context,
	                                         HASL_MECHANISM_ANONYMOUS_ERROR_MISSING_USERNAME);

	g_clear_object(&context);
}

static void
test_hasl_mechanism_anonymous_username_required_empty(void) {
	HaslContext *context = NULL;

	context = hasl_context_new();
	hasl_context_set_username(context, "");
	hasl_context_set_password(context, "hunter2");

	test_hasl_mechanism_anonymous_error_test(context,
	                                         HASL_MECHANISM_ANONYMOUS_ERROR_MISSING_USERNAME);

	g_clear_object(&context);
}

static void
test_hasl_mechanism_anonymous_possible_empty_context(void) {
	HaslContext *context = hasl_context_new();

	test_hasl_mechanism_anonymous_possible(context, FALSE, TRUE,
	                                       HASL_MECHANISM_ANONYMOUS_ERROR_MISSING_USERNAME);

	g_clear_object(&context);
}

static void
test_hasl_mechanism_anonymous_possible_username(void) {
	HaslContext *context = hasl_context_new();

	hasl_context_set_tls(context, TRUE);
	hasl_context_set_username(context, "alice");

	test_hasl_mechanism_anonymous_possible(context, TRUE, FALSE, 0);

	g_clear_object(&context);
}

static void
test_hasl_mechanism_anonymous_possible_allow_plain_in_clear_without_tls(void) {
	HaslContext *context = hasl_context_new();

	hasl_context_set_username(context, "alice");
	hasl_context_set_allow_clear_text(context, TRUE);

	test_hasl_mechanism_anonymous_possible(context, TRUE, FALSE, 0);

	g_clear_object(&context);
}

static void
test_hasl_mechanism_anonymous_possible_allow_plain_in_clear_with_tls(void) {
	HaslContext *context = hasl_context_new();

	hasl_context_set_username(context, "alice");
	hasl_context_set_allow_clear_text(context, TRUE);
	hasl_context_set_tls(context, TRUE);

	test_hasl_mechanism_anonymous_possible(context, TRUE, FALSE, 0);

	g_clear_object(&context);
}

static void
test_hasl_mechanism_anonymous_possible_allow_plain_in_clear_required(void) {
	HaslContext *context = hasl_context_new();

	hasl_context_set_username(context, "alice");
	hasl_context_set_tls(context, FALSE);

	test_hasl_mechanism_anonymous_possible(context, FALSE, TRUE,
	                                       HASL_MECHANISM_ANONYMOUS_ERROR_CLEARTEXT_NOT_ALLOWED);

	g_clear_object(&context);
}

/******************************************************************************
 * Main
 *****************************************************************************/
int
main(int argc, char *argv[]) {
	g_test_init(&argc, &argv, NULL);

	g_test_add_func("/hasl/mechanism-anonymous/new",
	                test_hasl_mechanism_anonymous_new);

	g_test_add_func("/hasl/mechanism-anonymous/username-required/null",
	                test_hasl_mechanism_anonymous_username_required_null);
	g_test_add_func("/hasl/mechanism-anonymous/username-required/empty",
	                test_hasl_mechanism_anonymous_username_required_empty);

	g_test_add_func("/hasl/mechanism-anonymous/possible/empty-context",
	                test_hasl_mechanism_anonymous_possible_empty_context);
	g_test_add_func("/hasl/mechanism-anonymous/possible/username",
	                test_hasl_mechanism_anonymous_possible_username);

	g_test_add_func("/hasl/mechanism-anonymous/possible/allow-plain-in-clear-without-tls",
	                test_hasl_mechanism_anonymous_possible_allow_plain_in_clear_without_tls);
	g_test_add_func("/hasl/mechanism-anonymous/possible/allow-plain-in-clear-with-tls",
	                test_hasl_mechanism_anonymous_possible_allow_plain_in_clear_with_tls);
	g_test_add_func("/hasl/mechanism-anonymous/possible/allow-plain-in-clear-required",
	                test_hasl_mechanism_anonymous_possible_allow_plain_in_clear_required);

	return g_test_run();
}
