<commit_before>package konnectors

import (
	"encoding/json"
	"os"
	"strings"
	"sync"
	"testing"

	"github.com/cozy/cozy-stack/pkg/apps"
	"github.com/cozy/cozy-stack/pkg/config"
	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/crypto"
	"github.com/cozy/cozy-stack/pkg/instance"
	"github.com/cozy/cozy-stack/pkg/jobs"
	"github.com/cozy/cozy-stack/pkg/permissions"
	"github.com/cozy/cozy-stack/pkg/realtime"
	"github.com/cozy/cozy-stack/tests/testutils"
	"github.com/spf13/afero"
	"github.com/stretchr/testify/assert"
	jwt "gopkg.in/dgrijalva/jwt-go.v3"
)

var inst *instance.Instance

func TestUnknownDomain(t *testing.T) {
	ctx := jobs.NewWorkerContext("unknown", "id")
	msg, err := jobs.NewMessage(jobs.JSONEncoding, map[string]interface{}{
		"slug":   "unknownapp",
		"fields": nil,
	})
	assert.NoError(t, err)
	err = Worker(ctx, msg)
	assert.Error(t, err)
	assert.Equal(t, "Instance not found", err.Error())
}

func TestUnknownApp(t *testing.T) {
	ctx := jobs.NewWorkerContext(inst.Domain, "id")
	msg, err := jobs.NewMessage(jobs.JSONEncoding, map[string]interface{}{
		"slug":   "unknownapp",
		"fields": nil,
	})
	assert.NoError(t, err)
	err = Worker(ctx, msg)
	assert.Error(t, err)
	assert.Equal(t, "Application is not installed", err.Error())
}

func TestBadFileExec(t *testing.T) {
	fields, err := json.Marshal(&struct{ Password string }{Password: "<PASSWORD>"})
	assert.NoError(t, err)

	installer, err := apps.NewInstaller(inst, inst.AppsCopier(apps.Konnector),
		&apps.InstallerOptions{
			Operation: apps.Install,
			Type:      apps.Konnector,
			Slug:      "my-konnector-1",
			SourceURL: "git://github.com/cozy/cozy-konnector-trainline.git",
		},
	)
	if !assert.NoError(t, err) {
		return
	}
	_, err = installer.RunSync()
	if !assert.NoError(t, err) {
		return
	}

	ctx := jobs.NewWorkerContext(inst.Domain, "id")
	msg, err := jobs.NewMessage(jobs.JSONEncoding, map[string]interface{}{
		"slug":   "my-konnector-1",
		"fields": fields,
	})
	assert.NoError(t, err)

	config.GetConfig().Konnectors.Cmd = ""
	err = Worker(ctx, msg)
	assert.Error(t, err)
	assert.Equal(t, "fork/exec : no such file or directory", err.Error())

	config.GetConfig().Konnectors.Cmd = "echo"
	err = Worker(ctx, msg)
	assert.NoError(t, err)
}

func TestSuccess(t *testing.T) {
	t.Skip()

	script := `#!/bin/bash

echo "{\"COZY_DOMAIN\":\"${COZY_DOMAIN}\", \"COZY_CREDENTIALS\":\"${COZY_CREDENTIALS}\"}"
echo "${COZY_FIELDS}"
echo "bad json"
echo "{\"Manifest\": \"$(ls ${1}/manifest.konnector)\"}"
>&2 echo "log error"
`
	osFs := afero.NewOsFs()
	tmpScript, err := afero.TempFile(osFs, "", "")
	if !assert.NoError(t, err) {
		return
	}
	defer osFs.RemoveAll(tmpScript.Name())

	err = afero.WriteFile(osFs, tmpScript.Name(), []byte(script), 0)
	if !assert.NoError(t, err) {
		return
	}

	err = osFs.Chmod(tmpScript.Name(), 0777)
	if !assert.NoError(t, err) {
		return
	}

	fields, err := json.Marshal(&struct{ Password string }{Password: "<PASSWORD>"})
	assert.NoError(t, err)

	installer, err := apps.NewInstaller(inst, inst.AppsCopier(apps.Konnector),
		&apps.InstallerOptions{
			Operation: apps.Install,
			Type:      apps.Konnector,
			Slug:      "my-konnector-2",
			SourceURL: "git://github.com/cozy/cozy-konnector-trainline.git",
		},
	)
	if !assert.NoError(t, err) {
		return
	}
	_, err = installer.RunSync()
	if !assert.NoError(t, err) {
		return
	}

	var wg sync.WaitGroup
	wg.Add(1)

	go func() {
		evCh := realtime.InstanceHub(inst.Domain).Subscribe(consts.JobEvents)
		ch := evCh.Read()
		ev1 := <-ch
		ev2 := <-ch
		ev3 := <-ch
		err = evCh.Close()
		assert.NoError(t, err)
		doc1 := ev1.Doc.(couchdb.JSONDoc)
		doc2 := ev2.Doc.(couchdb.JSONDoc)
		doc3 := ev3.Doc.(couchdb.JSONDoc)
		assert.Equal(t, inst.Domain, ev1.Instance)
		assert.Equal(t, inst.Domain, ev2.Instance)
		assert.Equal(t, inst.Domain, doc1.M["COZY_DOMAIN"])
		assert.Equal(t, "mypass", doc2.M["Password"])

		man := doc3.M["Manifest"].(string)
		assert.True(t, strings.HasPrefix(man, os.TempDir()))
		assert.True(t, strings.HasSuffix(man, "/manifest.konnector"))

		token := doc1.M["COZY_CREDENTIALS"].(string)
		var claims permissions.Claims
		err = crypto.ParseJWT(token, func(t *jwt.Token) (interface{}, error) {
			return inst.PickKey(t.Claims.(*permissions.Claims).Audience)
		}, &claims)
		assert.NoError(t, err)
		assert.Equal(t, permissions.KonnectorAudience, claims.Audience)
		wg.Done()
	}()

	ctx := jobs.NewWorkerContext(inst.Domain, "id")
	msg, err := jobs.NewMessage(jobs.JSONEncoding, map[string]interface{}{
		"slug":   "my-konnector-2",
		"fields": fields,
	})
	assert.NoError(t, err)

	config.GetConfig().Konnectors.Cmd = tmpScript.Name()
	err = Worker(ctx, msg)
	assert.NoError(t, err)

	wg.Wait()
}

func TestMain(m *testing.M) {
	config.UseTestFile()
	setup := testutils.NewSetup(m, "konnector_test")
	inst = setup.GetTestInstance()
	os.Exit(setup.Run())
}
<commit_msg>Retry tests<commit_after>package konnectors

import (
	"os"
	"strings"
	"sync"
	"testing"

	"github.com/cozy/cozy-stack/pkg/apps"
	"github.com/cozy/cozy-stack/pkg/config"
	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/crypto"
	"github.com/cozy/cozy-stack/pkg/instance"
	"github.com/cozy/cozy-stack/pkg/jobs"
	"github.com/cozy/cozy-stack/pkg/permissions"
	"github.com/cozy/cozy-stack/pkg/realtime"
	"github.com/cozy/cozy-stack/tests/testutils"
	"github.com/spf13/afero"
	"github.com/stretchr/testify/assert"
	jwt "gopkg.in/dgrijalva/jwt-go.v3"
)

var inst *instance.Instance

func TestUnknownDomain(t *testing.T) {
	ctx := jobs.NewWorkerContext("unknown", "id")
	msg, err := jobs.NewMessage(jobs.JSONEncoding, map[string]interface{}{
		"slug":   "unknownapp",
		"fields": nil,
	})
	assert.NoError(t, err)
	err = Worker(ctx, msg)
	assert.Error(t, err)
	assert.Equal(t, "Instance not found", err.Error())
}

func TestUnknownApp(t *testing.T) {
	ctx := jobs.NewWorkerContext(inst.Domain, "id")
	msg, err := jobs.NewMessage(jobs.JSONEncoding, map[string]interface{}{
		"slug":   "unknownapp",
		"fields": nil,
	})
	assert.NoError(t, err)
	err = Worker(ctx, msg)
	assert.Error(t, err)
	assert.Equal(t, "Application is not installed", err.Error())
}

func TestBadFileExec(t *testing.T) {
	fields := &struct{ Password string }{Password: "<PASSWORD>"}

	installer, err := apps.NewInstaller(inst, inst.AppsCopier(apps.Konnector),
		&apps.InstallerOptions{
			Operation: apps.Install,
			Type:      apps.Konnector,
			Slug:      "my-konnector-1",
			SourceURL: "git://github.com/cozy/cozy-konnector-trainline.git",
		},
	)
	if !assert.NoError(t, err) {
		return
	}
	_, err = installer.RunSync()
	if !assert.NoError(t, err) {
		return
	}

	ctx := jobs.NewWorkerContext(inst.Domain, "id")
	msg, err := jobs.NewMessage(jobs.JSONEncoding, map[string]interface{}{
		"slug":   "my-konnector-1",
		"fields": fields,
	})
	assert.NoError(t, err)

	config.GetConfig().Konnectors.Cmd = ""
	err = Worker(ctx, msg)
	assert.Error(t, err)
	assert.Equal(t, "fork/exec : no such file or directory", err.Error())

	config.GetConfig().Konnectors.Cmd = "echo"
	err = Worker(ctx, msg)
	assert.NoError(t, err)
}

func TestSuccess(t *testing.T) {
	script := `#!/bin/bash

echo "{\"COZY_DOMAIN\":\"${COZY_DOMAIN}\", \"COZY_CREDENTIALS\":\"${COZY_CREDENTIALS}\"}"
echo "${COZY_FIELDS}"
echo "bad json"
echo "{\"Manifest\": \"$(ls ${1}/manifest.konnector)\"}"
>&2 echo "log error"
`
	osFs := afero.NewOsFs()
	tmpScript, err := afero.TempFile(osFs, "", "")
	if !assert.NoError(t, err) {
		return
	}
	defer osFs.RemoveAll(tmpScript.Name())

	err = afero.WriteFile(osFs, tmpScript.Name(), []byte(script), 0)
	if !assert.NoError(t, err) {
		return
	}

	err = osFs.Chmod(tmpScript.Name(), 0777)
	if !assert.NoError(t, err) {
		return
	}

	fields := &struct{ Password string }{Password: "<PASSWORD>"}

	installer, err := apps.NewInstaller(inst, inst.AppsCopier(apps.Konnector),
		&apps.InstallerOptions{
			Operation: apps.Install,
			Type:      apps.Konnector,
			Slug:      "my-konnector-2",
			SourceURL: "git://github.com/cozy/cozy-konnector-trainline.git",
		},
	)
	if !assert.NoError(t, err) {
		return
	}
	_, err = installer.RunSync()
	if !assert.NoError(t, err) {
		return
	}

	var wg sync.WaitGroup
	wg.Add(1)

	go func() {
		evCh := realtime.InstanceHub(inst.Domain).Subscribe(consts.JobEvents)
		ch := evCh.Read()
		ev1 := <-ch
		ev2 := <-ch
		ev3 := <-ch
		err = evCh.Close()
		assert.NoError(t, err)
		doc1 := ev1.Doc.(couchdb.JSONDoc)
		doc2 := ev2.Doc.(couchdb.JSONDoc)
		doc3 := ev3.Doc.(couchdb.JSONDoc)
		assert.Equal(t, inst.Domain, ev1.Instance)
		assert.Equal(t, inst.Domain, ev2.Instance)
		assert.Equal(t, inst.Domain, doc1.M["COZY_DOMAIN"])
		assert.Equal(t, "mypass", doc2.M["Password"])

		man := doc3.M["Manifest"].(string)
		assert.True(t, strings.HasPrefix(man, os.TempDir()))
		assert.True(t, strings.HasSuffix(man, "/manifest.konnector"))

		token := doc1.M["COZY_CREDENTIALS"].(string)
		var claims permissions.Claims
		err = crypto.ParseJWT(token, func(t *jwt.Token) (interface{}, error) {
			return inst.PickKey(t.Claims.(*permissions.Claims).Audience)
		}, &claims)
		assert.NoError(t, err)
		assert.Equal(t, permissions.KonnectorAudience, claims.Audience)
		wg.Done()
	}()

	ctx := jobs.NewWorkerContext(inst.Domain, "id")
	msg, err := jobs.NewMessage(jobs.JSONEncoding, map[string]interface{}{
		"slug":   "my-konnector-2",
		"fields": fields,
	})
	assert.NoError(t, err)

	config.GetConfig().Konnectors.Cmd = tmpScript.Name()
	err = Worker(ctx, msg)
	assert.NoError(t, err)

	wg.Wait()
}

func TestMain(m *testing.M) {
	config.UseTestFile()
	setup := testutils.NewSetup(m, "konnector_test")
	inst = setup.GetTestInstance()
	os.Exit(setup.Run())
}

{'fraction_non_alphanumeric': 0.18443212158554118, 'fraction_numerical': 0.0061614294516327784, 'mean_word_length': 4.324767632586113, 'pattern_counts': {'":': 22, '<': 13, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5517466', 'n_tokens_mistral': 4142, 'n_tokens_neox': 3929, 'n_words': 643}