#!/bin/bash
set -e
. tests/lib

t-dependencies T2U jq
t-restrict x-tag2upload-service-manager

t-debpolicy

t-t2u-settings
t-t2u-native-prep

# Run this test case with real wall clock time
unset GIT_COMMITTER_DATE
unset GIT_AUTHOR_DATE

: ---------- "start the tag2upload-service-manager daemon" ----------

mkdir ../t2usm
cd ../t2usm

mkdir tmp

cat >t2usm.toml <<END

    [rocket]
    log_level = "debug"
    cli_colors = false
    port = 0

    [t2u]
    distro = "test-dummy"

    [log]
    level = "trace"

    [vhosts]
    ui = ["*"]
    webhook = ["*"]

    [files]
    db = "t2usm.db"
    scratch_dir = "tmp"
    o2m_socket = "o2m.s"
    port_report_file = "port"
    archive_dir = "t2usm-archive"

    [email]
    sendmail_program = "$troot/tstunt/sendmail"
    from_addr = "test-dummy t2u service t2usm <noreply@example.org>"
    reply_to_addr = "reply-to@example.org"
    copies_addr = "copies@example.org"

    [timeouts]
    socket_stat_interval = "30 s"

    [gitlab]
    poll_max_queue = 0

    [retry]
    # These parameters arrange for t2usm to retry immediately, but only once.
    # That provides predictability for retriable error test cases.
    timeout_initial = "0 s"
    timeout_increase = 1
    timeout_mintotal = "0 s"
    min_retries = 1
    min_salient_retries = 0

    [[t2u.forges]]
    kind = "gitlab"
    host = "gitlab.test-dummy.example.org"
    allow = ["*"]

    [testing]
    exit_hup_pgrp = false
    fake_https_dir = "$t2u_fake_https_dir"

END

# We have two independent ways of trying to ensure
# that the t2usm process doesn't outlive this script.
#   1. end_kill_pids
#   2. It will state `o2m_socket` every `socket_stat_interval`.
#      Since that's a relative path, if this directory is removed,
#      it will get ENOENT and quit - even if a new directory
#      with the same name is created.

mkdir t2usm-archive

t-mkfifo port
exec 3<>port
# we pass O_RDWR fd to t2usm; that way we get EOF if it crashes

$DGIT_TEST_T2USM_PROGRAM -c t2usm.toml run-manager &
t2usm_pid=$!
end_kill_pids+=" $! "

# we don't keep the O_RDWR fd, only an O_RDONLY one
exec 4<port
exec 3<&-
read <&4 port

cd ..

t-mkfifo check-for-banner
exec 3<>check-for-banner
nc.openbsd -U t2usm/o2m.s >check-for-banner &
exec 3<&-
read banner <check-for-banner

test "$banner" = "t2u-manager-ready"

curl http://127.0.0.1:$port/ >page-front-start.html

: ---------- "start the tag2upload oracle daemon" ----------

start-oracled () {
    t-t2u-exec-t2u-oracled			\
	    --ssh="$troot/ssh"			\
	    --manager=t2u-service-manager-host	\
	    --no-restart-workers		\
	    --force-production			\
	    --manager-socket=$tmp/t2usm/o2m.s &
    oracled_pid=$!
    # We don't care about cleanup of this forked oracled:
    # If the manager dies, so will the oracled.
}

start-oracled

poll1-for-worker-in-front-page () {
    curl http://127.0.0.1:$port/ >page-front-worker.html
    if grep -P '\bt2u-b,' page-front-worker.html; then t-poll-done; fi
}
t-poll-loop 1000 poll1-for-worker-in-front-page
# ^ when we abandon gnupg, set this timeout to 5

: ---------- "make an upload tag and push it to fake salsa" ----------

cd $p

t-t2u-setup-repo

t-sendmail-seq-reset

t-git-debpush

: ---------- "simulate webhook" ----------

simulate-webhook () {
    tagname=test-dummy/$v
    dep14tagref="refs/tags/$tagname"
    tag_objid=$(t-git-get-ref-exact $dep14tagref)
    hook_message=$(git cat-file tag $tagname | sed '1,/^$/d' | jq -sR)

    # Prepare the "api call responses" for tag fetches :

    fake_tag_dir="$t2u_fake_https_dir/$t2u_fake_salsa_host/api/v4/projects/$t2u_fake_salsa_project_id/repository/tags"
    mkdir -p -- "$fake_tag_dir"

    for tag in $(git tag -l); do
	fake_tag_file="$fake_tag_dir/${tag//\//%2F}"
	tag_date=$(TZ=UTC git for-each-ref "$dep14tagref" \
	    --format='%(creatordate:format-local:%Y-%m-%dT%H:%M:%SZ)')
	tag_date="${tag_date/+/.000+}"
	cat <<END >"$fake_tag_file"
    {
      "target": "$tag_objid",
      "created_at": "$tag_date"
    }
END
    done

    # Make the web hook request:

    curl http://127.0.0.1:"$port"/hook/gitlab -d@- <<END | tee ../hook-response
    {
      "event_name": "tag_push",
      "object_kind": "tag_push",
      "after": "$tag_objid",
      "ref": "refs/tags/test-dummy/$v",
      "project": {
	"git_http_url": "file://$t2u_fake_salsa_repo",
	"id": $t2u_fake_salsa_project_id
      },
      "user_id": 25598,
      "message": $hook_message
    }
END

    jid=$(sed -n 's/^job received, jid=//p' <../hook-response)
    test "x$jid" != x
}

simulate-webhook

cd ..

poll-for-pattern-in-queued-page () {
    local slug=$1
    local pattern=$2
    curl http://127.0.0.1:$port/recent >page-recent-$slug.html
    if grep -P -e "$pattern" page-recent-$slug.html; then t-poll-done; fi
}

t-poll-loop 1000 poll-for-pattern-in-queued-page processing example
# ^ when we abandon gnupg, set this timeout to 8

: ---------- "await completion and check it worked" ----------

t-poll-loop 1000 poll-for-pattern-in-queued-page done Uploaded
# ^ when we abandon gnupg, set this timeout to 80

cd $p

t-t2u-succeeded

check-status-emailed () {
    local estatus="$1"; shift

    t-poll-loop 1000 t-t2u-poll-for-job-status $jid $estatus Sent
    t-t2u-check-emailed-exactly "$@"
}

check-status-emailed Uploaded			\
    list starting				\
    user uploaded

# Fetch the dgit view - in particular, the archive/ tag, from dgit-repos
t-dgit fetch
t-pushed-good master

# Check that git tag in dgit-repos is the original one from git-debpush
t-refs-same-start
t-ref-same-val git-debpush $tag_objid
cd ../git/$p.git
t-ref-same-exact $dep14tagref

cd $tmp/$p

: ========== "error cases" ==========

# Check that the user always gets exactly one email, and that the list
# gets "starting" emails and reports of errors that will be retried.
# The user report sometimes gets sent only to the `tagger`,  It gets sent
# to the tagger and the signer if we successfully verified the signature.

: ---------- "error case: transient early" ----------

t-sendmail-seq-reset
t-dch-commit-bump 'transient early'

t-git-debpush
exec 3>$tmp/reboot-lock-1
flock -xn 3
simulate-webhook
wait $oracled_pid || echo $?
start-oracled
wait $oracled_pid || echo $?

check-status-emailed Failed			\
    tagger failed

exec 3<&- # release lock

: ---------- "error case: builder fetch tempfail" ----------

# Conveniently, at this point oracled isn't running

# we let t2usm fetch, then sabotage the repo for oracled/builder
t-sendmail-seq-reset
t-dch-commit-bump 'builder fetch tempfail'
t-git-debpush
simulate-webhook
t-poll-loop 10 t-t2u-poll-for-job-status $jid Queued

# moving this aside is a tempfail, not permfail, because curl doesn't
# simulate a 404 for ENOENT.  That's what we want for this test.
mv "$t2u_fake_salsa_repo"{,.aside}
cd ..
start-oracled
cd $p

t-poll-loop 1000 t-t2u-poll-for-job-status $jid Failed

mv "$t2u_fake_salsa_repo"{.aside,} # un-sabotage

check-status-emailed Failed			\
    list starting				\
    list retriable				\
    list starting				\
    user failed

: ---------- "error case: builder fetch permfail" ----------

# again, we let t2usm fetch, then sabotage the repo
t-kill $oracled_pid
wait $oracled_pid || : $?
t-sendmail-seq-reset
t-dch-commit-bump 'builder fetch permfail'
t-git-debpush
simulate-webhook
t-poll-loop 10 t-t2u-poll-for-job-status $jid Queued

cd "$t2u_fake_salsa_repo"
git tag -d test-dummy/$v
cd $tmp
start-oracled
cd $p
t-poll-loop 1000 t-t2u-poll-for-job-status $jid Irrecoverable

check-status-emailed Irrecoverable		\
    list starting				\
    user irrecoverable

: ---------- "error case: bad tag signature" ----------

t-sendmail-seq-reset

git cat-file tag test-dummy/$v >../corrupted-tag
perl -i~ -pe 's{\b\Q'$v'\E}{$&.corrupted}g' ../corrupted-tag
v=$v.corrupted
badtag=$(git hash-object -w -t tag ../corrupted-tag)
git update-ref refs/tags/test-dummy/$v $badtag
git push salsa test-dummy/$v
simulate-webhook

t-poll-loop 1000 t-t2u-poll-for-job-status $jid Irrecoverable

check-status-emailed Irrecoverable		\
    list starting				\
    tagger rejected

: ---------- "all is correct" ----------

# We don't test t2usm fetch tempfail, or t2usm fetch permfail, here,
# because those don't involve dgit.git at all.  They are tested in
# tag2upload-service-manager.git.

# kill manager and wait for oracled to die.
# (Doing this explicitly here avoids termination output in the log,
# after the test has exited 0; such delayed output is confusing.)

t-kill $t2usm_pid
wait $oracled_pid || : $?
sleep 0.1 # give worker a chance to die too

t-ok
