/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */
/* Copyright (c) 2025 Brett A C Sheffield <bacs@librecast.net> */

/*
 * test channel actions
 *
 * When lcagent receives an authenticated packet, it looks up the channel it was
 * received on and executes the configured commands with the options specified.
 *
 * `lcagent exec` simulates receiving such a packet on the commandline.
 *
 * Test this, by:
 * - create test state
 * - call agent_exec() with the test state
 * - check the results
 */

#include "test.h"
#include <agent.h>
#include <unistd.h>

#define TEST_DATA \
	X(0, "non-binary") \
	X(1, "female") \
	X(2, "male") \
	X(3, "none of your business") \
	X(4, "why are you obsessed with toilets?") \
	X(5, "didn't you used to be an author?")

static char testfile[] = "0000-0023-XXXXXX";
static int fdtest;

static void push_test_item(state_t *state, char *chanstr)
{
	char cmd[128];
	state_push_channel(state, chanstr);
	snprintf(cmd, sizeof cmd, "echo -n \"%s\" > %s", chanstr, testfile);
	state_push_command(state, cmd, 0);
}

static void load_test_data(state_t *state)
{
	state_defaults_set(state);
#define X(a, b) push_test_item(state, b);
	TEST_DATA
#undef X
}

static int create_fakehome(char *fakehome)
{
	int rc;
	/* create fake home directory */
	if (!test_assert(mkdtemp(fakehome) != NULL, "mkdtemp()")) {
		perror("mkdtemp");
		return test_status;
	}
	rc = setenv("HOME", fakehome, 1);
	test_assert(rc == 0, "set HOME");
	return test_status;
}

static int test_exec_cmd(state_t *state, char *chanstr)
{
	char fakehome[] = "0000-0023-XXXXXX";
	char buf[BUFSIZ] = "";
	char *argv[] = { PACKAGE_NAME, "exec", chanstr, NULL };
	ssize_t byt;
	int argc = sizeof argv / sizeof argv[0] - 1;
	int rc;

	if (create_fakehome(fakehome)) return test_status;
	state_defaults_set(state);
	state_parse_args(state, argc, argv);

	rc = agent_run(state);
	test_assert(rc == EXIT_SUCCESS, "`lcagent exec \"%s\"` returned %i", chanstr, rc);

	test_log("buffer: '%s'\n", buf);

	/* verify cmd output */
	lseek(fdtest, 0, SEEK_SET);
	byt = read(fdtest, buf, sizeof buf);

	if (!test_assert(byt > 0, "read %zi bytes", byt)) return test_status;
	test_assert(strstr(buf, chanstr) != NULL, "buffer contains channel string '%s'", chanstr);

	return test_status;
}

static int test_exec_commands(void)
{
	state_t state = {0};

	fdtest = mkstemp(testfile);
	if (!test_assert(fdtest >= 0, "creating temp file '%s'", testfile))
		return test_status;

	load_test_data(&state);

#define X(a, b) if (test_exec_cmd(&state, b)) return test_status;
	TEST_DATA
#undef X

	free_state(&state);
	close(fdtest);

	return test_status;
}

static int test_exec_good_channel(void)
{
	char fakehome[] = "0000-0023-XXXXXX";
	state_t state = {0};
	char *argv[] = { PACKAGE_NAME, "exec", "none of your business", NULL };
	int argc = sizeof argv / sizeof argv[0] - 1;
	int rc;

	if (create_fakehome(fakehome)) return test_status;
	load_test_data(&state);

	rc = agent(&state, argc, argv);
	test_assert(rc == EXIT_SUCCESS, "lcagent exec (good channel) returned %i", rc);

	return test_status;
}

static int test_exec_bad_channel(void)
{
	char fakehome[] = "0000-0023-XXXXXX";
	state_t state = {0};
	char *argv[] = { PACKAGE_NAME, "exec", "no such channel", NULL };
	int argc = sizeof argv / sizeof argv[0] - 1;
	int rc;

	if (create_fakehome(fakehome)) return test_status;
	load_test_data(&state);

	rc = agent(&state, argc, argv);
	test_assert(rc == EXIT_FAILURE, "lcagent exec (bad channel) returned %i", rc);

	return test_status;
}

static int test_exec_noop(void)
{
	char fakehome[] = "0000-0023-XXXXXX";
	state_t state = {0};
	char *argv[] = { PACKAGE_NAME, "exec", NULL };
	int argc = sizeof argv / sizeof argv[0] - 1;
	int rc;

	if (create_fakehome(fakehome)) return test_status;
	rc = agent(&state, argc, argv);
	test_assert(rc == EXIT_SUCCESS, "lcagent exec (NOOP) returned %i", rc);

	return test_status;
}

int main(void)
{
	char name[] = "lcagent exec";

	test_name(name);

	if (test_exec_noop()) return test_status;
	if (test_exec_bad_channel()) return test_status;
	if (test_exec_good_channel()) return test_status;
	if (test_exec_commands()) return test_status;
	// TODO if (test_exec_command_stdin()) return test_status;

	return test_status;
}
