package main

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os"
	"strconv"
	"testing"

	activitypub "github.com/yukimochi/Activity-Relay/ActivityPub"
	state "github.com/yukimochi/Activity-Relay/State"
)

const (
	BlockService state.Config = iota
	ManuallyAccept
	CreateAsAnnounce
)

func TestHandleWebfingerGet(t *testing.T) {
	s := httptest.NewServer(http.HandlerFunc(handleWebfinger))
	defer s.Close()

	req, _ := http.NewRequest("GET", s.URL, nil)
	q := req.URL.Query()
	q.Add("resource", "acct:relay@"+hostURL.Host)
	req.URL.RawQuery = q.Encode()
	client := new(http.Client)
	r, err := client.Do(req)
	if err != nil {
		t.Fatalf("Failed - " + err.Error())
	}
	if r.Header.Get("Content-Type") != "application/json" {
		t.Fatalf("Failed - Content-Type not match.")
	}
	if r.StatusCode != 200 {
		t.Fatalf("Failed - StatusCode is not 200.")
	}
	defer r.Body.Close()

	data, _ := ioutil.ReadAll(r.Body)
	var wfresource activitypub.WebfingerResource
	err = json.Unmarshal(data, &wfresource)
	if err != nil {
		t.Fatalf("WebfingerResource response is not valid.")
	}

	domain, _ := url.Parse(wfresource.Links[0].Href)
	if domain.Host != hostURL.Host {
		t.Fatalf("WebfingerResource's Host not valid.")
	}
}

func TestHandleWebfingerGetBadResource(t *testing.T) {
	s := httptest.NewServer(http.HandlerFunc(handleWebfinger))
	defer s.Close()

	req, _ := http.NewRequest("GET", s.URL, nil)
	q := req.URL.Query()
	q.Add("resource", "acct:yukimochi@"+os.Getenv("RELAY_DOMAIN"))
	req.URL.RawQuery = q.Encode()
	client := new(http.Client)
	r, err := client.Do(req)
	if err != nil {
		t.Fatalf("Failed - " + err.Error())
	}
	if r.StatusCode != 404 {
		t.Fatalf("Failed - StatusCode is not 404.")
	}
}

func TestHandleNodeinfoLinkGet(t *testing.T) {
	s := httptest.NewServer(http.HandlerFunc(handleNodeinfoLink))
	defer s.Close()

	req, _ := http.NewRequest("GET", s.URL, nil)
	client := new(http.Client)
	r, err := client.Do(req)
	if err != nil {
		t.Fatalf("Failed - " + err.Error())
	}
	if r.Header.Get("Content-Type") != "application/json" {
		t.Fatalf("Failed - Content-Type not match.")
	}
	if r.StatusCode != 200 {
		t.Fatalf("Failed - StatusCode is not 200.")
	}
	defer r.Body.Close()

	data, _ := ioutil.ReadAll(r.Body)
	var nodeinfoLinks activitypub.NodeinfoLinks
	err = json.Unmarshal(data, &nodeinfoLinks)
	if err != nil {
		t.Fatalf("NodeinfoLinks response is not valid.")
	}
}

func TestHandleNodeinfoLinkInvalidMethod(t *testing.T) {
	s := httptest.NewServer(http.HandlerFunc(handleNodeinfoLink))
	defer s.Close()

	req, _ := http.NewRequest("POST", s.URL, nil)
	client := new(http.Client)
	r, err := client.Do(req)
	if err != nil {
		t.Fatalf("Failed - " + err.Error())
	}
	if r.StatusCode != 400 {
		t.Fatalf("Failed - StatusCode is not 400.")
	}
}

func TestHandleNodeinfoGet(t *testing.T) {
	s := httptest.NewServer(http.HandlerFunc(handleNodeinfo))
	defer s.Close()

	req, _ := http.NewRequest("GET", s.URL, nil)
	client := new(http.Client)
	r, err := client.Do(req)
	if err != nil {
		t.Fatalf("Failed - " + err.Error())
	}
	if r.Header.Get("Content-Type") != "application/json" {
		t.Fatalf("Failed - Content-Type not match.")
	}
	if r.StatusCode != 200 {
		t.Fatalf("Failed - StatusCode is not 200.")
	}
	defer r.Body.Close()

	data, _ := ioutil.ReadAll(r.Body)
	var nodeinfo activitypub.Nodeinfo
	err = json.Unmarshal(data, &nodeinfo)
	if err != nil {
		t.Fatalf("Nodeinfo response is not valid.")
	}
}

func TestHandleNodeinfoInvalidMethod(t *testing.T) {
	s := httptest.NewServer(http.HandlerFunc(handleNodeinfo))
	defer s.Close()

	req, _ := http.NewRequest("POST", s.URL, nil)
	client := new(http.Client)
	r, err := client.Do(req)
	if err != nil {
		t.Fatalf("Failed - " + err.Error())
	}
	if r.StatusCode != 400 {
		t.Fatalf("Failed - StatusCode is not 400.")
	}
}

func TestHandleWebfingerInvalidMethod(t *testing.T) {
	s := httptest.NewServer(http.HandlerFunc(handleWebfinger))
	defer s.Close()

	req, _ := http.NewRequest("POST", s.URL, nil)
	client := new(http.Client)
	r, err := client.Do(req)
	if err != nil {
		t.Fatalf("Failed - " + err.Error())
	}
	if r.StatusCode != 400 {
		t.Fatalf("Failed - StatusCode is not 400.")
	}
}

func TestHandleActorGet(t *testing.T) {
	s := httptest.NewServer(http.HandlerFunc(handleActor))
	defer s.Close()

	r, err := http.Get(s.URL)
	if err != nil {
		t.Fatalf("Failed - " + err.Error())
	}
	if r.Header.Get("Content-Type") != "application/activity+json" {
		t.Fatalf("Failed - Content-Type not match.")
	}
	if r.StatusCode != 200 {
		t.Fatalf("Failed - StatusCode is not 200.")
	}
	defer r.Body.Close()

	data, _ := ioutil.ReadAll(r.Body)
	var actor activitypub.Actor
	err = json.Unmarshal(data, &actor)
	if err != nil {
		t.Fatalf("Actor response is not valid.")
	}

	domain, _ := url.Parse(actor.ID)
	if domain.Host != hostURL.Host {
		t.Fatalf("Actor's Host not valid.")
	}
}

func TestHandleActorInvalidMethod(t *testing.T) {
	s := httptest.NewServer(http.HandlerFunc(handleActor))
	defer s.Close()

	r, err := http.Post(s.URL, "text/plain", nil)
	if err != nil {
		t.Fatalf("Failed - " + err.Error())
	}
	if r.StatusCode != 400 {
		t.Fatalf("Failed - StatusCode is not 400.")
	}
}

func TestContains(t *testing.T) {
	data := "nil"
	sData := []string{
		"no",
		"nil",
	}
	badData := 0
	result := contains(data, "true")
	if result != false {
		t.Fatalf("Failed - no contain but true.")
	}
	result = contains(data, "nil")
	if result != true {
		t.Fatalf("Failed - contain but false.")
	}
	result = contains(sData, "true")
	if result != false {
		t.Fatalf("Failed - no contain but true. (slice)")
	}
	result = contains(sData, "nil")
	if result != true {
		t.Fatalf("Failed - contain but false. (slice)")
	}
	result = contains(badData, "hoge")
	if result != false {
		t.Fatalf("Failed - input bad data but true. (slice)")
	}
}

func mockActivityDecoderProvider(activity *activitypub.Activity, actor *activitypub.Actor) func(r *http.Request) (*activitypub.Activity, *activitypub.Actor, []byte, error) {
	return func(r *http.Request) (*activitypub.Activity, *activitypub.Actor, []byte, error) {
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			log.Fatal(err)
		}

		return activity, actor, body, nil
	}
}

func mockActivity(req string) activitypub.Activity {
	switch req {
	case "Follow":
		file, _ := os.Open("./misc/follow.json")
		body, _ := ioutil.ReadAll(file)
		var activity activitypub.Activity
		json.Unmarshal(body, &activity)
		return activity
	case "Invalid-Follow":
		file, _ := os.Open("./misc/followAsActor.json")
		body, _ := ioutil.ReadAll(file)
		var activity activitypub.Activity
		json.Unmarshal(body, &activity)
		return activity
	case "Unfollow":
		file, _ := os.Open("./misc/unfollow.json")
		body, _ := ioutil.ReadAll(file)
		var activity activitypub.Activity
		json.Unmarshal(body, &activity)
		return activity
	case "Invalid-Unfollow":
		body := "{\"@context\":\"https://www.w3.org/ns/activitystreams\",\"id\":\"https://mastodon.test.yukimochi.io/c125e836-e622-478e-a22d-2d9fbf2f496f\",\"type\":\"Undo\",\"actor\":\"https://mastodon.test.yukimochi.io/users/yukimochi\",\"object\":{\"@context\":\"https://www.w3.org/ns/activitystreams\",\"id\":\"https://hacked.test.yukimochi.io/c125e836-e622-478e-a22d-2d9fbf2f496f\",\"type\":\"Follow\",\"actor\":\"https://hacked.test.yukimochi.io/users/yukimochi\",\"object\":\"https://www.w3.org/ns/activitystreams#Public\"}}"
		var activity activitypub.Activity
		json.Unmarshal([]byte(body), &activity)
		return activity
	case "UnfollowAsActor":
		body := "{\"@context\":\"https://www.w3.org/ns/activitystreams\",\"id\":\"https://mastodon.test.yukimochi.io/c125e836-e622-478e-a22d-2d9fbf2f496f\",\"type\":\"Undo\",\"actor\":\"https://mastodon.test.yukimochi.io/users/yukimochi\",\"object\":{\"@context\":\"https://www.w3.org/ns/activitystreams\",\"id\":\"https://hacked.test.yukimochi.io/c125e836-e622-478e-a22d-2d9fbf2f496f\",\"type\":\"Follow\",\"actor\":\"https://mastodon.test.yukimochi.io/users/yukimochi\",\"object\":\"https://relay.yukimochi.example.org/actor\"}}"
		var activity activitypub.Activity
		json.Unmarshal([]byte(body), &activity)
		return activity
	case "Create":
		file, _ := os.Open("./misc/create.json")
		body, _ := ioutil.ReadAll(file)
		var activity activitypub.Activity
		json.Unmarshal(body, &activity)
		return activity
	case "Create-Article":
		body := "{\"@context\":[\"https://www.w3.org/ns/activitystreams\",\"https://w3id.org/security/v1\",{\"manuallyApprovesFollowers\":\"as:manuallyApprovesFollowers\",\"sensitive\":\"as:sensitive\",\"movedTo\":{\"@id\":\"as:movedTo\",\"@type\":\"@id\"},\"Hashtag\":\"as:Hashtag\",\"ostatus\":\"http://ostatus.org#\",\"atomUri\":\"ostatus:atomUri\",\"inReplyToAtomUri\":\"ostatus:inReplyToAtomUri\",\"conversation\":\"ostatus:conversation\",\"toot\":\"http://joinmastodon.org/ns#\",\"Emoji\":\"toot:Emoji\",\"focalPoint\":{\"@container\":\"@list\",\"@id\":\"toot:focalPoint\"},\"featured\":{\"@id\":\"toot:featured\",\"@type\":\"@id\"},\"schema\":\"http://schema.org#\",\"PropertyValue\":\"schema:PropertyValue\",\"value\":\"schema:value\"}],\"id\":\"https://mastodon.test.yukimochi.io/users/yukimochi/statuses/101075045564444857/activity\",\"type\":\"Create\",\"actor\":\"https://mastodon.test.yukimochi.io/users/yukimochi\",\"published\":\"2018-11-15T11:07:26Z\",\"to\":[\"https://www.w3.org/ns/activitystreams#Public\"],\"cc\":[\"https://mastodon.test.yukimochi.io/users/yukimochi/followers\"],\"object\":{\"id\":\"https://mastodon.test.yukimochi.io/users/yukimochi/statuses/101075045564444857\",\"type\":\"Article\",\"summary\":null,\"inReplyTo\":null,\"published\":\"2018-11-15T11:07:26Z\",\"url\":\"https://mastodon.test.yukimochi.io/@yukimochi/101075045564444857\",\"attributedTo\":\"https://mastodon.test.yukimochi.io/users/yukimochi\",\"to\":[\"https://www.w3.org/ns/activitystreams#Public\"],\"cc\":[\"https://mastodon.test.yukimochi.io/users/yukimochi/followers\"],\"sensitive\":false,\"atomUri\":\"https://mastodon.test.yukimochi.io/users/yukimochi/statuses/101075045564444857\",\"inReplyToAtomUri\":null,\"conversation\":\"tag:mastodon.test.yukimochi.io,2018-11-15:objectId=68:objectType=Conversation\",\"content\":\"<p>Actvity-Relay</p>\",\"contentMap\":{\"en\":\"<p>Actvity-Relay</p>\"},\"attachment\":[],\"tag\":[]},\"signature\":{\"type\":\"RsaSignature2017\",\"creator\":\"https://mastodon.test.yukimochi.io/users/yukimochi#main-key\",\"created\":\"2018-11-15T11:07:26Z\",\"signatureValue\":\"mMgl2GgVPgb1Kw6a2iDIZc7r0j3ob+Cl9y+QkCxIe6KmnUzb15e60UuhkE5j3rJnoTwRKqOFy1PMkSxlYW6fPG/5DBxW9I4kX+8sw8iH/zpwKKUOnXUJEqfwRrNH2ix33xcs/GkKPdedY6iAPV9vGZ10MSMOdypfYgU9r+UI0sTaaC2iMXH0WPnHQuYAI+Q1JDHIbDX5FH1WlDL6+8fKAicf3spBMxDwPHGPK8W2jmDLWdN2Vz4ffsCtWs5BCuqOKZrtTW0Rdd4HWzo40MnRXvBjv7yNlnnKzokANBqiOLWT7kNfK0+Vtnt6c/bNX64KBro53KR7wL3ZBvPVuv5rdQ==\"}}"
		var activity activitypub.Activity
		json.Unmarshal([]byte(body), &activity)
		return activity
	case "Announce":
		file, _ := os.Open("./misc/announce.json")
		body, _ := ioutil.ReadAll(file)
		var activity activitypub.Activity
		json.Unmarshal(body, &activity)
		return activity
	case "Undo":
		file, _ := os.Open("./misc/undo.json")
		body, _ := ioutil.ReadAll(file)
		var activity activitypub.Activity
		json.Unmarshal(body, &activity)
		return activity
	default:
		panic("No assined request.")
	}
}

func mockActor(req string) activitypub.Actor {
	switch req {
	case "Person":
		file, _ := os.Open("./misc/person.json")
		body, _ := ioutil.ReadAll(file)
		var actor activitypub.Actor
		json.Unmarshal(body, &actor)
		return actor
	case "Service":
		file, _ := os.Open("./misc/service.json")
		body, _ := ioutil.ReadAll(file)
		var actor activitypub.Actor
		json.Unmarshal(body, &actor)
		return actor
	case "Application":
		file, _ := os.Open("./misc/application.json")
		body, _ := ioutil.ReadAll(file)
		var actor activitypub.Actor
		json.Unmarshal(body, &actor)
		return actor
	default:
		panic("No assined request.")
	}
}

func TestSuitableRelayNoBlockService(t *testing.T) {
	activity := mockActivity("Create")
	personActor := mockActor("Person")
	serviceActor := mockActor("Service")
	applicationActor := mockActor("Application")

	relayState.SetConfig(BlockService, false)

	if suitableRelay(&activity, &personActor) != true {
		t.Fatalf("Failed - Person status not relay")
	}
	if suitableRelay(&activity, &serviceActor) != true {
		t.Fatalf("Failed - Service status not relay")
	}
	if suitableRelay(&activity, &applicationActor) != true {
		t.Fatalf("Failed - Service status not relay")
	}
}

func TestSuitableRelayBlockService(t *testing.T) {
	activity := mockActivity("Create")
	personActor := mockActor("Person")
	serviceActor := mockActor("Service")
	applicationActor := mockActor("Application")

	relayState.SetConfig(BlockService, true)

	if suitableRelay(&activity, &personActor) != true {
		t.Fatalf("Failed - Person status not relay")
	}
	if suitableRelay(&activity, &serviceActor) != false {
		t.Fatalf("Failed - Service status may relay when blocking mode")
	}
	if suitableRelay(&activity, &applicationActor) != false {
		t.Fatalf("Failed - Application status may relay when blocking mode")
	}
	relayState.SetConfig(BlockService, false)
}

func TestHandleInboxNoSignature(t *testing.T) {
	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		handleInbox(w, r, decodeActivity)
	}))
	defer s.Close()

	req, _ := http.NewRequest("POST", s.URL, nil)
	client := new(http.Client)
	r, err := client.Do(req)
	if err != nil {
		t.Fatalf("Failed - " + err.Error())
	}
	if r.StatusCode != 400 {
		t.Fatalf("Failed - StatusCode is not 400")
	}
}

func TestHandleInboxInvalidMethod(t *testing.T) {
	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		handleInbox(w, r, decodeActivity)
	}))
	defer s.Close()

	req, _ := http.NewRequest("GET", s.URL, nil)
	client := new(http.Client)
	r, err := client.Do(req)
	if err != nil {
		t.Fatalf("Failed - " + err.Error())
	}
	if r.StatusCode != 404 {
		t.Fatalf("Failed - StatusCode is not 404")
	}
}

func TestHandleInboxValidFollow(t *testing.T) {
	activity := mockActivity("Follow")
	actor := mockActor("Person")
	domain, _ := url.Parse(activity.Actor)
	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		handleInbox(w, r, mockActivityDecoderProvider(&activity, &actor))
	}))
	defer s.Close()

	req, _ := http.NewRequest("POST", s.URL, nil)
	client := new(http.Client)
	r, err := client.Do(req)
	if err != nil {
		t.Fatalf("Failed - " + err.Error())
	}
	if r.StatusCode != 202 {
		t.Fatalf("Failed - StatusCode is not 202 - " + strconv.Itoa(r.StatusCode))
	}
	res, _ := relayState.RedisClient.Exists("relay:subscription:" + domain.Host).Result()
	if res != 1 {
		t.Fatalf("Failed - Subscription not works.")
	}
	relayState.DelSubscription(domain.Host)
}

func TestHandleInboxValidManuallyFollow(t *testing.T) {
	activity := mockActivity("Follow")
	actor := mockActor("Person")
	domain, _ := url.Parse(activity.Actor)
	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		handleInbox(w, r, mockActivityDecoderProvider(&activity, &actor))
	}))
	defer s.Close()

	// Switch Manually
	relayState.SetConfig(ManuallyAccept, true)

	req, _ := http.NewRequest("POST", s.URL, nil)
	client := new(http.Client)
	r, err := client.Do(req)
	if err != nil {
		t.Fatalf("Failed - " + err.Error())
	}
	if r.StatusCode != 202 {
		t.Fatalf("Failed - StatusCode is not 202 - " + strconv.Itoa(r.StatusCode))
	}
	res, _ := relayState.RedisClient.Exists("relay:pending:" + domain.Host).Result()
	if res != 1 {
		t.Fatalf("Failed - Pending not works.")
	}
	res, _ = relayState.RedisClient.Exists("relay:subscription:" + domain.Host).Result()
	if res != 0 {
		t.Fatalf("Failed - Pending was skipped.")
	}
	relayState.DelSubscription(domain.Host)
	relayState.SetConfig(ManuallyAccept, false)
}

func TestHandleInboxInvalidFollow(t *testing.T) {
	activity := mockActivity("Invalid-Follow")
	actor := mockActor("Person")
	domain, _ := url.Parse(activity.Actor)
	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		handleInbox(w, r, mockActivityDecoderProvider(&activity, &actor))
	}))
	defer s.Close()

	relayState.SetConfig(ManuallyAccept, false)

	req, _ := http.NewRequest("POST", s.URL, nil)
	client := new(http.Client)
	r, err := client.Do(req)
	if err != nil {
		t.Fatalf("Failed - " + err.Error())
	}
	if r.StatusCode != 202 {
		t.Fatalf("Failed - StatusCode is not 202 - " + strconv.Itoa(r.StatusCode))
	}
	res, _ := relayState.RedisClient.Exists("relay:subscription:" + domain.Host).Result()
	if res != 0 {
		t.Fatalf("Failed - Subscription not blocked.")
	}
}

func TestHandleInboxValidFollowBlocked(t *testing.T) {
	activity := mockActivity("Follow")
	actor := mockActor("Person")
	domain, _ := url.Parse(activity.Actor)
	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		handleInbox(w, r, mockActivityDecoderProvider(&activity, &actor))
	}))
	defer s.Close()

	relayState.SetBlockedDomain(domain.Host, true)

	req, _ := http.NewRequest("POST", s.URL, nil)
	client := new(http.Client)
	r, err := client.Do(req)
	if err != nil {
		t.Fatalf("Failed - " + err.Error())
	}
	if r.StatusCode != 202 {
		t.Fatalf("Failed - StatusCode is not 202 - " + strconv.Itoa(r.StatusCode))
	}
	res, _ := relayState.RedisClient.Exists("relay:subscription:" + domain.Host).Result()
	if res != 0 {
		t.Fatalf("Failed - Subscription not blocked.")
	}
	relayState.DelSubscription(domain.Host)
	relayState.SetBlockedDomain(domain.Host, false)
}

func TestHandleInboxValidUnfollow(t *testing.T) {
	activity := mockActivity("Unfollow")
	actor := mockActor("Person")
	domain, _ := url.Parse(activity.Actor)
	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		handleInbox(w, r, mockActivityDecoderProvider(&activity, &actor))
	}))
	defer s.Close()

	relayState.AddSubscription(state.Subscription{
		Domain:   domain.Host,
		InboxURL: "https://mastodon.test.yukimochi.io/inbox",
	})

	req, _ := http.NewRequest("POST", s.URL, nil)
	client := new(http.Client)
	r, err := client.Do(req)
	if err != nil {
		t.Fatalf("Failed - " + err.Error())
	}
	if r.StatusCode != 202 {
		t.Fatalf("Failed - StatusCode is not 202 - " + strconv.Itoa(r.StatusCode))
	}
	res, _ := relayState.RedisClient.Exists("relay:subscription:" + domain.Host).Result()
	if res != 0 {
		t.Fatalf("Failed - Subscription not succeed.")
	}
	relayState.DelSubscription(domain.Host)
}

func TestHandleInboxInvalidUnfollow(t *testing.T) {
	activity := mockActivity("Invalid-Unfollow")
	actor := mockActor("Person")
	domain, _ := url.Parse(activity.Actor)
	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		handleInbox(w, r, mockActivityDecoderProvider(&activity, &actor))
	}))
	defer s.Close()

	relayState.AddSubscription(state.Subscription{
		Domain:   domain.Host,
		InboxURL: "https://mastodon.test.yukimochi.io/inbox",
	})

	req, _ := http.NewRequest("POST", s.URL, nil)
	client := new(http.Client)
	r, err := client.Do(req)
	if err != nil {
		t.Fatalf("Failed - " + err.Error())
	}
	if r.StatusCode != 400 {
		t.Fatalf("Failed - StatusCode is not 400")
	}
	res, _ := relayState.RedisClient.Exists("relay:subscription:" + domain.Host).Result()
	if res != 1 {
		t.Fatalf("Failed - Block hacked unfollow not succeed.")
	}
	relayState.DelSubscription(domain.Host)
}

func TestHandleInboxUnfollowAsActor(t *testing.T) {
	activity := mockActivity("UnfollowAsActor")
	actor := mockActor("Person")
	domain, _ := url.Parse(activity.Actor)
	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		handleInbox(w, r, mockActivityDecoderProvider(&activity, &actor))
	}))
	defer s.Close()

	relayState.AddSubscription(state.Subscription{
		Domain:   domain.Host,
		InboxURL: "https://mastodon.test.yukimochi.io/inbox",
	})

	req, _ := http.NewRequest("POST", s.URL, nil)
	client := new(http.Client)
	r, err := client.Do(req)
	if err != nil {
		t.Fatalf("Failed - " + err.Error())
	}
	if r.StatusCode != 400 {
		t.Fatalf("Failed - StatusCode is not 400")
	}
	res, _ := relayState.RedisClient.Exists("relay:subscription:" + domain.Host).Result()
	if res != 1 {
		t.Fatalf("Failed - Block actor unfollow not succeed.")
	}
	relayState.DelSubscription(domain.Host)
}

func TestHandleInboxValidCreate(t *testing.T) {
	activity := mockActivity("Create")
	actor := mockActor("Person")
	domain, _ := url.Parse(activity.Actor)
	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		handleInbox(w, r, mockActivityDecoderProvider(&activity, &actor))
	}))
	defer s.Close()

	relayState.AddSubscription(state.Subscription{
		Domain:   domain.Host,
		InboxURL: "https://mastodon.test.yukimochi.io/inbox",
	})
	relayState.AddSubscription(state.Subscription{
		Domain:   "example.org",
		InboxURL: "https://example.org/inbox",
	})

	req, _ := http.NewRequest("POST", s.URL, nil)
	client := new(http.Client)
	r, err := client.Do(req)
	if err != nil {
		t.Fatalf("Failed - " + err.Error())
	}
	if r.StatusCode != 202 {
		t.Fatalf("Failed - StatusCode is not 202 - " + strconv.Itoa(r.StatusCode))
	}
	relayState.DelSubscription(domain.Host)
	relayState.DelSubscription("example.org")
	relayState.RedisClient.Del("relay:subscription:" + domain.Host).Result()
	relayState.RedisClient.Del("relay:subscription:example.org").Result()
}

func TestHandleInboxlimitedCreate(t *testing.T) {
	activity := mockActivity("Create")
	actor := mockActor("Person")
	domain, _ := url.Parse(activity.Actor)
	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		handleInbox(w, r, mockActivityDecoderProvider(&activity, &actor))
	}))
	defer s.Close()

	relayState.AddSubscription(state.Subscription{
		Domain:   domain.Host,
		InboxURL: "https://mastodon.test.yukimochi.io/inbox",
	})
	relayState.SetLimitedDomain(domain.Host, true)

	req, _ := http.NewRequest("POST", s.URL, nil)
	client := new(http.Client)
	r, err := client.Do(req)
	if err != nil {
		t.Fatalf("Failed - " + err.Error())
	}
	if r.StatusCode != 202 {
		t.Fatalf("Failed - StatusCode is not 202 - " + strconv.Itoa(r.StatusCode))
	}
	relayState.DelSubscription(domain.Host)
	relayState.SetLimitedDomain(domain.Host, false)
}

func TestHandleInboxValidCreateAsAnnounceNote(t *testing.T) {
	activity := mockActivity("Create")
	actor := mockActor("Person")
	domain, _ := url.Parse(activity.Actor)
	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		handleInbox(w, r, mockActivityDecoderProvider(&activity, &actor))
	}))
	defer s.Close()

	relayState.AddSubscription(state.Subscription{
		Domain:   domain.Host,
		InboxURL: "https://mastodon.test.yukimochi.io/inbox",
	})
	relayState.AddSubscription(state.Subscription{
		Domain:   "example.org",
		InboxURL: "https://example.org/inbox",
	})
	relayState.SetConfig(CreateAsAnnounce, true)

	req, _ := http.NewRequest("POST", s.URL, nil)
	client := new(http.Client)
	r, err := client.Do(req)
	if err != nil {
		t.Fatalf("Failed - " + err.Error())
	}
	if r.StatusCode != 202 {
		t.Fatalf("Failed - StatusCode is not 202 - " + strconv.Itoa(r.StatusCode))
	}
	relayState.DelSubscription(domain.Host)
	relayState.DelSubscription("example.org")
	relayState.SetConfig(CreateAsAnnounce, false)
}

func TestHandleInboxValidCreateAsAnnounceNoNote(t *testing.T) {
	activity := mockActivity("Create-Article")
	actor := mockActor("Person")
	domain, _ := url.Parse(activity.Actor)
	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		handleInbox(w, r, mockActivityDecoderProvider(&activity, &actor))
	}))
	defer s.Close()

	relayState.AddSubscription(state.Subscription{
		Domain:   domain.Host,
		InboxURL: "https://mastodon.test.yukimochi.io/inbox",
	})
	relayState.AddSubscription(state.Subscription{
		Domain:   "example.org",
		InboxURL: "https://example.org/inbox",
	})
	relayState.SetConfig(CreateAsAnnounce, true)

	req, _ := http.NewRequest("POST", s.URL, nil)
	client := new(http.Client)
	r, err := client.Do(req)
	if err != nil {
		t.Fatalf("Failed - " + err.Error())
	}
	if r.StatusCode != 202 {
		t.Fatalf("Failed - StatusCode is not 202 - " + strconv.Itoa(r.StatusCode))
	}
	relayState.DelSubscription(domain.Host)
	relayState.DelSubscription("example.org")
	relayState.SetConfig(CreateAsAnnounce, false)
}

func TestHandleInboxUnsubscriptionCreate(t *testing.T) {
	activity := mockActivity("Create")
	actor := mockActor("Person")
	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		handleInbox(w, r, mockActivityDecoderProvider(&activity, &actor))
	}))
	defer s.Close()

	req, _ := http.NewRequest("POST", s.URL, nil)
	client := new(http.Client)
	r, err := client.Do(req)
	if err != nil {
		t.Fatalf("Failed - " + err.Error())
	}
	if r.StatusCode != 400 {
		t.Fatalf("Failed - StatusCode is not 400")
	}
}

func TestHandleInboxUndo(t *testing.T) {
	activity := mockActivity("Undo")
	actor := mockActor("Person")
	domain, _ := url.Parse(activity.Actor)
	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		handleInbox(w, r, mockActivityDecoderProvider(&activity, &actor))
	}))
	defer s.Close()

	relayState.AddSubscription(state.Subscription{
		Domain:   domain.Host,
		InboxURL: "https://mastodon.test.yukimochi.io/inbox",
	})

	req, _ := http.NewRequest("POST", s.URL, nil)
	client := new(http.Client)
	r, err := client.Do(req)
	if err != nil {
		t.Fatalf("Failed - " + err.Error())
	}
	if r.StatusCode != 202 {
		t.Fatalf("Failed - StatusCode is not 202 - " + strconv.Itoa(r.StatusCode))
	}
	res, _ := relayState.RedisClient.Exists("relay:subscription:" + domain.Host).Result()
	if res != 1 {
		t.Fatalf("Failed - Missing unsubscribed.")
	}
	relayState.DelSubscription(domain.Host)
}
