<commit_before>package files

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os"
	"strconv"
	"strings"
	"sync/atomic"
	"testing"

	"github.com/cozy/cozy-stack/couchdb"
	"github.com/cozy/cozy-stack/couchdb/mango"
	"github.com/cozy/cozy-stack/vfs"
	"github.com/cozy/cozy-stack/web/middlewares"
	"github.com/gin-gonic/gin"
	"github.com/sourcegraph/checkup"
	"github.com/spf13/afero"
	"github.com/stretchr/testify/assert"
)

const CouchURL = "http://localhost:5984/"
const TestPrefix = "test/"

var ts *httptest.Server
var instance *middlewares.Instance

func injectInstance(instance *middlewares.Instance) gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Set("instance", instance)
	}
}

func extractJSONRes(res *http.Response, mp *map[string]interface{}) (err error) {
	if res.StatusCode >= 300 {
		return
	}

	var b []byte

	b, err = ioutil.ReadAll(res.Body)
	if err != nil {
		return
	}

	err = json.Unmarshal(b, mp)
	return
}

func createDir(t *testing.T, path string) (res *http.Response, v map[string]interface{}) {
	res, err := http.Post(ts.URL+path, "text/plain", strings.NewReader(""))
	if !assert.NoError(t, err) {
		return
	}
	defer res.Body.Close()

	err = extractJSONRes(res, &v)
	assert.NoError(t, err)

	return
}

func doUploadOrMod(t *testing.T, req *http.Request, contentType, body, hash string) (res *http.Response, v map[string]interface{}) {
	var err error

	if contentType != "" {
		req.Header.Add("Content-Type", contentType)
	}

	if hash != "" {
		req.Header.Add("Content-MD5", hash)
	}

	res, err = http.DefaultClient.Do(req)
	if !assert.NoError(t, err) {
		return
	}

	defer res.Body.Close()

	err = extractJSONRes(res, &v)
	assert.NoError(t, err)

	return
}

func upload(t *testing.T, path, contentType, body, hash string) (res *http.Response, v map[string]interface{}) {
	buf := strings.NewReader(body)
	req, err := http.NewRequest("POST", ts.URL+path, buf)
	if !assert.NoError(t, err) {
		return
	}
	return doUploadOrMod(t, req, contentType, body, hash)
}

func uploadMod(t *testing.T, path, contentType, body, hash string) (res *http.Response, v map[string]interface{}) {
	buf := strings.NewReader(body)
	req, err := http.NewRequest("PUT", ts.URL+path, buf)
	if !assert.NoError(t, err) {
		return
	}
	return doUploadOrMod(t, req, contentType, body, hash)
}

func extractDirData(t *testing.T, data map[string]interface{}) (string, map[string]interface{}) {
	var ok bool

	data, ok = data["data"].(map[string]interface{})
	if !assert.True(t, ok) {
		return "", nil
	}

	id, ok := data["id"].(string)
	if !assert.True(t, ok) {
		return "", nil
	}

	return id, data
}

func patchFile(t *testing.T, path, docType, id string, attrs map[string]interface{}) (res *http.Response, v map[string]interface{}) {
	type jsonData struct {
		Type  string                 `json:"type"`
		ID    string                 `json:"id"`
		Attrs map[string]interface{} `json:"attributes,omitempty"`
	}

	bodyreq := &jsonData{
		Type:  docType,
		ID:    id,
		Attrs: attrs,
	}

	b, err := json.Marshal(map[string]*jsonData{"data": bodyreq})
	req, err := http.NewRequest("PATCH", ts.URL+path, bytes.NewReader(b))
	if !assert.NoError(t, err) {
		return
	}

	res, err = http.DefaultClient.Do(req)
	if !assert.NoError(t, err) {
		return
	}

	defer res.Body.Close()

	err = extractJSONRes(res, &v)
	assert.NoError(t, err)

	return
}

func download(t *testing.T, path, byteRange string) (res *http.Response, body []byte) {
	req, err := http.NewRequest("GET", ts.URL+path, nil)
	if !assert.NoError(t, err) {
		return
	}

	if byteRange != "" {
		req.Header.Add("Range", byteRange)
	}

	res, err = http.DefaultClient.Do(req)
	if !assert.NoError(t, err) {
		return
	}

	body, err = ioutil.ReadAll(res.Body)
	if !assert.NoError(t, err) {
		return
	}

	return
}

func TestCreateDirWithNoType(t *testing.T) {
	res, _ := createDir(t, "/files/")
	assert.Equal(t, 422, res.StatusCode)
}

func TestCreateDirWithNoName(t *testing.T) {
	res, _ := createDir(t, "/files/?Type=io.cozy.folders")
	assert.Equal(t, 422, res.StatusCode)
}

func TestCreateDirOnNonExistingParent(t *testing.T) {
	res, _ := createDir(t, "/files/noooop?Name=foo&Type=io.cozy.folders")
	assert.Equal(t, 404, res.StatusCode)
}

func TestCreateDirAlreadyExists(t *testing.T) {
	res1, _ := createDir(t, "/files/?Name=iexist&Type=io.cozy.folders")
	assert.Equal(t, 201, res1.StatusCode)

	res2, _ := createDir(t, "/files/?Name=iexist&Type=io.cozy.folders")
	assert.Equal(t, 409, res2.StatusCode)
}

func TestCreateDirRootSuccess(t *testing.T) {
	res, _ := createDir(t, "/files/?Name=coucou&Type=io.cozy.folders")
	assert.Equal(t, 201, res.StatusCode)

	storage, _ := instance.GetStorageProvider()
	exists, err := afero.DirExists(storage, "/coucou")
	assert.NoError(t, err)
	assert.True(t, exists)
}

func TestCreateDirWithParentSuccess(t *testing.T) {
	res1, data1 := createDir(t, "/files/?Name=dirparent&Type=io.cozy.folders")
	assert.Equal(t, 201, res1.StatusCode)

	var ok bool
	data1, ok = data1["data"].(map[string]interface{})
	assert.True(t, ok)

	parentID, ok := data1["id"].(string)
	assert.True(t, ok)

	res2, _ := createDir(t, "/files/"+parentID+"?Name=child&Type=io.cozy.folders")
	assert.Equal(t, 201, res2.StatusCode)

	storage, _ := instance.GetStorageProvider()
	exists, err := afero.DirExists(storage, "/dirparent/child")
	assert.NoError(t, err)
	assert.True(t, exists)
}

func TestCreateDirWithIllegalCharacter(t *testing.T) {
	res1, _ := createDir(t, "/files/?Name=coucou/les/copains!&Type=io.cozy.folders")
	assert.Equal(t, 422, res1.StatusCode)

	res2, _ := createDir(t, "/files/?Name=j'ai\x00untrou!&Type=io.cozy.folders")
	assert.Equal(t, 422, res2.StatusCode)
}

func TestCreateDirConcurrently(t *testing.T) {
	done := make(chan *http.Response)
	errs := make(chan *http.Response)

	doCreateDir := func(name string) {
		res, _ := createDir(t, "/files/?Name="+name+"&Type=io.cozy.folders")
		if res.StatusCode == 201 {
			done <- res
		} else {
			errs <- res
		}
	}

	n := 100
	c := 0

	for i := 0; i < n; i++ {
		go doCreateDir("foo")
	}

	for i := 0; i < n; i++ {
		select {
		case res := <-errs:
			assert.Equal(t, 409, res.StatusCode)
		case <-done:
			c = c + 1
		}
	}

	assert.Equal(t, 1, c)
}

func TestUploadWithNoType(t *testing.T) {
	res, _ := upload(t, "/files/", "text/plain", "foo", "")
	assert.Equal(t, 422, res.StatusCode)
}

func TestUploadWithNoName(t *testing.T) {
	res, _ := upload(t, "/files/?Type=io.cozy.files", "text/plain", "foo", "")
	assert.Equal(t, 422, res.StatusCode)
}

func TestUploadBadHash(t *testing.T) {
	body := "foo"
	res, _ := upload(t, "/files/?Type=io.cozy.files&Name=badhash", "text/plain", body, "3FbbMXfH+PdjAlWFfVb1dQ==")
	assert.Equal(t, 412, res.StatusCode)

	storage, _ := instance.GetStorageProvider()
	_, err := afero.ReadFile(storage, "/badhash")
	assert.Error(t, err)
}

func TestUploadAtRootSuccess(t *testing.T) {
	body := "foo"
	res, _ := upload(t, "/files/?Type=io.cozy.files&Name=goodhash", "text/plain", body, "rL0Y20zC+Fzt72VPzMSk2A==")
	assert.Equal(t, 201, res.StatusCode)

	storage, _ := instance.GetStorageProvider()
	buf, err := afero.ReadFile(storage, "/goodhash")
	assert.NoError(t, err)
	assert.Equal(t, body, string(buf))
}

func TestUploadConcurrently(t *testing.T) {
	done := make(chan *http.Response)
	errs := make(chan *http.Response)

	doUpload := func(name, body string) {
		res, _ := upload(t, "/files/?Type=io.cozy.files&Name="+name, "text/plain", body, "")
		if res.StatusCode == 201 {
			done <- res
		} else {
			errs <- res
		}
	}

	n := 100
	c := 0

	for i := 0; i < n; i++ {
		go doUpload("uploadedconcurrently", "body "+strconv.Itoa(i))
	}

	for i := 0; i < n; i++ {
		select {
		case res := <-errs:
			assert.Equal(t, 409, res.StatusCode)
		case <-done:
			c = c + 1
		}
	}

	assert.Equal(t, 1, c)
}

func TestUploadWithParentSuccess(t *testing.T) {
	res1, data1 := createDir(t, "/files/?Name=fileparent&Type=io.cozy.folders")
	assert.Equal(t, 201, res1.StatusCode)

	var ok bool
	data1, ok = data1["data"].(map[string]interface{})
	assert.True(t, ok)

	parentID, ok := data1["id"].(string)
	assert.True(t, ok)

	body := "foo"
	res2, _ := upload(t, "/files/"+parentID+"?Type=io.cozy.files&Name=goodhash", "text/plain", body, "rL0Y20zC+Fzt72VPzMSk2A==")
	assert.Equal(t, 201, res2.StatusCode)

	storage, _ := instance.GetStorageProvider()
	buf, err := afero.ReadFile(storage, "/fileparent/goodhash")
	assert.NoError(t, err)
	assert.Equal(t, body, string(buf))
}

func TestUploadAtRootAlreadyExists(t *testing.T) {
	body := "foo"
	res1, _ := upload(t, "/files/?Type=io.cozy.files&Name=iexistfile", "text/plain", body, "rL0Y20zC+Fzt72VPzMSk2A==")
	assert.Equal(t, 201, res1.StatusCode)

	res2, _ := upload(t, "/files/?Type=io.cozy.files&Name=iexistfile", "text/plain", body, "rL0Y20zC+Fzt72VPzMSk2A==")
	assert.Equal(t, 409, res2.StatusCode)
}

func TestUploadWithParentAlreadyExists(t *testing.T) {
	_, dirdata := createDir(t, "/files/?Type=io.cozy.folders&Name=container")

	var ok bool
	dirdata, ok = dirdata["data"].(map[string]interface{})
	assert.True(t, ok)

	parentID, ok := dirdata["id"].(string)
	assert.True(t, ok)

	body := "foo"
	res1, _ := upload(t, "/files/"+parentID+"?Type=io.cozy.files&Name=iexistfile", "text/plain", body, "rL0Y20zC+Fzt72VPzMSk2A==")
	assert.Equal(t, 201, res1.StatusCode)

	res2, _ := upload(t, "/files/"+parentID+"?Type=io.cozy.files&Name=iexistfile", "text/plain", body, "rL0Y20zC+Fzt72VPzMSk2A==")
	assert.Equal(t, 409, res2.StatusCode)
}

func TestModifyMetadataFileMove(t *testing.T) {
	body := "foo"
	res1, data1 := upload(t, "/files/?Type=io.cozy.files&Name=filemoveme&Tags=foo,bar", "text/plain", body, "rL0Y20zC+Fzt72VPzMSk2A==")
	assert.Equal(t, 201, res1.StatusCode)

	var ok bool
	data1, ok = data1["data"].(map[string]interface{})
	assert.True(t, ok)

	fileID, ok := data1["id"].(string)
	assert.True(t, ok)

	res2, data2 := createDir(t, "/files/?Name=movemeinme&Type=io.cozy.folders")
	assert.Equal(t, 201, res2.StatusCode)

	data2, ok = data2["data"].(map[string]interface{})
	assert.True(t, ok)

	folderID, ok := data2["id"].(string)
	assert.True(t, ok)

	attrs := map[string]interface{}{
		"tags":       []string{"bar", "bar", "baz"},
		"name":       "moved",
		"folder_id":  folderID,
		"executable": true,
	}

	res3, data3 := patchFile(t, "/files/"+fileID, "io.cozy.files", fileID, attrs)
	assert.Equal(t, 200, res3.StatusCode)

	data3, ok = data3["data"].(map[string]interface{})
	assert.True(t, ok)

	attrs3, ok := data3["attributes"].(map[string]interface{})
	assert.True(t, ok)

	assert.Equal(t, "text/plain", attrs3["mime"])
	assert.Equal(t, "moved", attrs3["name"])
	assert.EqualValues(t, []interface{}{"bar", "baz"}, attrs3["tags"])
	assert.Equal(t, "text", attrs3["class"])
	assert.Equal(t, "rL0Y20zC+Fzt72VPzMSk2A==", attrs3["md5sum"])
	assert.Equal(t, true, attrs3["executable"])
	assert.Equal(t, "3", attrs3["size"])
}

func TestModifyMetadataFileConflict(t *testing.T) {
	body := "foo"
	res1, data1 := upload(t, "/files/?Type=io.cozy.files&Name=fmodme1&Tags=foo,bar", "text/plain", body, "rL0Y20zC+Fzt72VPzMSk2A==")
	assert.Equal(t, 201, res1.StatusCode)

	res2, _ := upload(t, "/files/?Type=io.cozy.files&Name=fmodme2&Tags=foo,bar", "text/plain", body, "rL0Y20zC+Fzt72VPzMSk2A==")
	assert.Equal(t, 201, res2.StatusCode)

	file1ID, _ := extractDirData(t, data1)

	attrs := map[string]interface{}{
		"name": "fmodme2",
	}

	res3, _ := patchFile(t, "/files/"+file1ID, "io.cozy.files", file1ID, attrs)
	assert.Equal(t, 409, res3.StatusCode)
}

func TestModifyMetadataDirMove(t *testing.T) {
	res1, data1 := createDir(t, "/files/?Name=dirmodme&Type=io.cozy.folders&Tags=foo,bar,bar")
	assert.Equal(t, 201, res1.StatusCode)

	folder1ID, _ := extractDirData(t, data1)

	reschild1, _ := createDir(t, "/files/"+folder1ID+"?Name=child1&Type=io.cozy.folders")
	assert.Equal(t, 201, reschild1.StatusCode)

	reschild2, _ := createDir(t, "/files/"+folder1ID+"?Name=child2&Type=io.cozy.folders")
	assert.Equal(t, 201, reschild2.StatusCode)

	res2, data2 := createDir(t, "/files/?Name=dirmodmemoveinme&Type=io.cozy.folders")
	assert.Equal(t, 201, res2.StatusCode)

	folder2ID, _ := extractDirData(t, data2)

	attrs1 := map[string]interface{}{
		"tags":      []string{"bar", "baz"},
		"name":      "renamed",
		"folder_id": folder2ID,
	}

	res3, _ := patchFile(t, "/files/"+folder1ID, "io.cozy.folders", folder1ID, attrs1)
	assert.Equal(t, 200, res3.StatusCode)

	storage, _ := instance.GetStorageProvider()
	exists, err := afero.DirExists(storage, "/dirmodmemoveinme/renamed")
	assert.NoError(t, err)
	assert.True(t, exists)

	attrs2 := map[string]interface{}{
		"tags":      []string{"bar", "baz"},
		"name":      "renamed",
		"folder_id": folder1ID,
	}

	res4, _ := patchFile(t, "/files/"+folder2ID, "io.cozy.folders", folder2ID, attrs2)
	assert.Equal(t, 412, res4.StatusCode)

	res5, _ := patchFile(t, "/files/"+folder1ID, "io.cozy.folders", folder1ID, attrs2)
	assert.Equal(t, 412, res5.StatusCode)
}

func TestModifyMetadataDirMoveConflict(t *testing.T) {
	res1, _ := createDir(t, "/files/?Name=conflictmodme1&Type=io.cozy.folders&Tags=foo,bar,bar")
	assert.Equal(t, 201, res1.StatusCode)

	res2, data2 := createDir(t, "/files/?Name=conflictmodme2&Type=io.cozy.folders")
	assert.Equal(t, 201, res2.StatusCode)

	folder2ID, _ := extractDirData(t, data2)

	attrs1 := map[string]interface{}{
		"tags": []string{"bar", "baz"},
		"name": "conflictmodme1",
	}

	res3, _ := patchFile(t, "/files/"+folder2ID, "io.cozy.folders", folder2ID, attrs1)
	assert.Equal(t, 409, res3.StatusCode)
}

func TestModifyContentNoFileID(t *testing.T) {
	res, _ := uploadMod(t, "/files/badid", "text/plain", "nil", "")
	assert.Equal(t, 404, res.StatusCode)
}
<commit_msg>Add test to move file with jsonapi<commit_after>package files

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os"
	"strconv"
	"strings"
	"sync/atomic"
	"testing"

	"github.com/cozy/cozy-stack/couchdb"
	"github.com/cozy/cozy-stack/couchdb/mango"
	"github.com/cozy/cozy-stack/vfs"
	"github.com/cozy/cozy-stack/web/middlewares"
	"github.com/gin-gonic/gin"
	"github.com/sourcegraph/checkup"
	"github.com/spf13/afero"
	"github.com/stretchr/testify/assert"
)

const CouchURL = "http://localhost:5984/"
const TestPrefix = "test/"

var ts *httptest.Server
var instance *middlewares.Instance

func injectInstance(instance *middlewares.Instance) gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Set("instance", instance)
	}
}

func extractJSONRes(res *http.Response, mp *map[string]interface{}) (err error) {
	if res.StatusCode >= 300 {
		return
	}

	var b []byte

	b, err = ioutil.ReadAll(res.Body)
	if err != nil {
		return
	}

	err = json.Unmarshal(b, mp)
	return
}

func createDir(t *testing.T, path string) (res *http.Response, v map[string]interface{}) {
	res, err := http.Post(ts.URL+path, "text/plain", strings.NewReader(""))
	if !assert.NoError(t, err) {
		return
	}
	defer res.Body.Close()

	err = extractJSONRes(res, &v)
	assert.NoError(t, err)

	return
}

func doUploadOrMod(t *testing.T, req *http.Request, contentType, body, hash string) (res *http.Response, v map[string]interface{}) {
	var err error

	if contentType != "" {
		req.Header.Add("Content-Type", contentType)
	}

	if hash != "" {
		req.Header.Add("Content-MD5", hash)
	}

	res, err = http.DefaultClient.Do(req)
	if !assert.NoError(t, err) {
		return
	}

	defer res.Body.Close()

	err = extractJSONRes(res, &v)
	assert.NoError(t, err)

	return
}

func upload(t *testing.T, path, contentType, body, hash string) (res *http.Response, v map[string]interface{}) {
	buf := strings.NewReader(body)
	req, err := http.NewRequest("POST", ts.URL+path, buf)
	if !assert.NoError(t, err) {
		return
	}
	return doUploadOrMod(t, req, contentType, body, hash)
}

func uploadMod(t *testing.T, path, contentType, body, hash string) (res *http.Response, v map[string]interface{}) {
	buf := strings.NewReader(body)
	req, err := http.NewRequest("PUT", ts.URL+path, buf)
	if !assert.NoError(t, err) {
		return
	}
	return doUploadOrMod(t, req, contentType, body, hash)
}

func extractDirData(t *testing.T, data map[string]interface{}) (string, map[string]interface{}) {
	var ok bool

	data, ok = data["data"].(map[string]interface{})
	if !assert.True(t, ok) {
		return "", nil
	}

	id, ok := data["id"].(string)
	if !assert.True(t, ok) {
		return "", nil
	}

	return id, data
}

type relsParent struct {
	parent *jsonData
}

type jsonData struct {
	Type  string                 `json:"type"`
	ID    string                 `json:"id"`
	Attrs map[string]interface{} `json:"attributes,omitempty"`
	Rels  map[string]interface{} `json:"relationships,omitempty"`
}

func patchFile(t *testing.T, path, docType, id string, attrs map[string]interface{}, parent *jsonData) (res *http.Response, v map[string]interface{}) {
	bodyreq := &jsonData{
		Type:  docType,
		ID:    id,
		Attrs: attrs,
	}

	if parent != nil {
		bodyreq.Rels = map[string]interface{}{
			"parent": map[string]interface{}{
				"data": parent,
			},
		}
	}

	b, err := json.Marshal(map[string]*jsonData{"data": bodyreq})
	req, err := http.NewRequest("PATCH", ts.URL+path, bytes.NewReader(b))
	if !assert.NoError(t, err) {
		return
	}

	res, err = http.DefaultClient.Do(req)
	if !assert.NoError(t, err) {
		return
	}

	defer res.Body.Close()

	err = extractJSONRes(res, &v)
	assert.NoError(t, err)

	return
}

func download(t *testing.T, path, byteRange string) (res *http.Response, body []byte) {
	req, err := http.NewRequest("GET", ts.URL+path, nil)
	if !assert.NoError(t, err) {
		return
	}

	if byteRange != "" {
		req.Header.Add("Range", byteRange)
	}

	res, err = http.DefaultClient.Do(req)
	if !assert.NoError(t, err) {
		return
	}

	body, err = ioutil.ReadAll(res.Body)
	if !assert.NoError(t, err) {
		return
	}

	return
}

func TestCreateDirWithNoType(t *testing.T) {
	res, _ := createDir(t, "/files/")
	assert.Equal(t, 422, res.StatusCode)
}

func TestCreateDirWithNoName(t *testing.T) {
	res, _ := createDir(t, "/files/?Type=io.cozy.folders")
	assert.Equal(t, 422, res.StatusCode)
}

func TestCreateDirOnNonExistingParent(t *testing.T) {
	res, _ := createDir(t, "/files/noooop?Name=foo&Type=io.cozy.folders")
	assert.Equal(t, 404, res.StatusCode)
}

func TestCreateDirAlreadyExists(t *testing.T) {
	res1, _ := createDir(t, "/files/?Name=iexist&Type=io.cozy.folders")
	assert.Equal(t, 201, res1.StatusCode)

	res2, _ := createDir(t, "/files/?Name=iexist&Type=io.cozy.folders")
	assert.Equal(t, 409, res2.StatusCode)
}

func TestCreateDirRootSuccess(t *testing.T) {
	res, _ := createDir(t, "/files/?Name=coucou&Type=io.cozy.folders")
	assert.Equal(t, 201, res.StatusCode)

	storage, _ := instance.GetStorageProvider()
	exists, err := afero.DirExists(storage, "/coucou")
	assert.NoError(t, err)
	assert.True(t, exists)
}

func TestCreateDirWithParentSuccess(t *testing.T) {
	res1, data1 := createDir(t, "/files/?Name=dirparent&Type=io.cozy.folders")
	assert.Equal(t, 201, res1.StatusCode)

	var ok bool
	data1, ok = data1["data"].(map[string]interface{})
	assert.True(t, ok)

	parentID, ok := data1["id"].(string)
	assert.True(t, ok)

	res2, _ := createDir(t, "/files/"+parentID+"?Name=child&Type=io.cozy.folders")
	assert.Equal(t, 201, res2.StatusCode)

	storage, _ := instance.GetStorageProvider()
	exists, err := afero.DirExists(storage, "/dirparent/child")
	assert.NoError(t, err)
	assert.True(t, exists)
}

func TestCreateDirWithIllegalCharacter(t *testing.T) {
	res1, _ := createDir(t, "/files/?Name=coucou/les/copains!&Type=io.cozy.folders")
	assert.Equal(t, 422, res1.StatusCode)

	res2, _ := createDir(t, "/files/?Name=j'ai\x00untrou!&Type=io.cozy.folders")
	assert.Equal(t, 422, res2.StatusCode)
}

func TestCreateDirConcurrently(t *testing.T) {
	done := make(chan *http.Response)
	errs := make(chan *http.Response)

	doCreateDir := func(name string) {
		res, _ := createDir(t, "/files/?Name="+name+"&Type=io.cozy.folders")
		if res.StatusCode == 201 {
			done <- res
		} else {
			errs <- res
		}
	}

	n := 100
	c := 0

	for i := 0; i < n; i++ {
		go doCreateDir("foo")
	}

	for i := 0; i < n; i++ {
		select {
		case res := <-errs:
			assert.Equal(t, 409, res.StatusCode)
		case <-done:
			c = c + 1
		}
	}

	assert.Equal(t, 1, c)
}

func TestUploadWithNoType(t *testing.T) {
	res, _ := upload(t, "/files/", "text/plain", "foo", "")
	assert.Equal(t, 422, res.StatusCode)
}

func TestUploadWithNoName(t *testing.T) {
	res, _ := upload(t, "/files/?Type=io.cozy.files", "text/plain", "foo", "")
	assert.Equal(t, 422, res.StatusCode)
}

func TestUploadBadHash(t *testing.T) {
	body := "foo"
	res, _ := upload(t, "/files/?Type=io.cozy.files&Name=badhash", "text/plain", body, "3FbbMXfH+PdjAlWFfVb1dQ==")
	assert.Equal(t, 412, res.StatusCode)

	storage, _ := instance.GetStorageProvider()
	_, err := afero.ReadFile(storage, "/badhash")
	assert.Error(t, err)
}

func TestUploadAtRootSuccess(t *testing.T) {
	body := "foo"
	res, _ := upload(t, "/files/?Type=io.cozy.files&Name=goodhash", "text/plain", body, "rL0Y20zC+Fzt72VPzMSk2A==")
	assert.Equal(t, 201, res.StatusCode)

	storage, _ := instance.GetStorageProvider()
	buf, err := afero.ReadFile(storage, "/goodhash")
	assert.NoError(t, err)
	assert.Equal(t, body, string(buf))
}

func TestUploadConcurrently(t *testing.T) {
	done := make(chan *http.Response)
	errs := make(chan *http.Response)

	doUpload := func(name, body string) {
		res, _ := upload(t, "/files/?Type=io.cozy.files&Name="+name, "text/plain", body, "")
		if res.StatusCode == 201 {
			done <- res
		} else {
			errs <- res
		}
	}

	n := 100
	c := 0

	for i := 0; i < n; i++ {
		go doUpload("uploadedconcurrently", "body "+strconv.Itoa(i))
	}

	for i := 0; i < n; i++ {
		select {
		case res := <-errs:
			assert.Equal(t, 409, res.StatusCode)
		case <-done:
			c = c + 1
		}
	}

	assert.Equal(t, 1, c)
}

func TestUploadWithParentSuccess(t *testing.T) {
	res1, data1 := createDir(t, "/files/?Name=fileparent&Type=io.cozy.folders")
	assert.Equal(t, 201, res1.StatusCode)

	var ok bool
	data1, ok = data1["data"].(map[string]interface{})
	assert.True(t, ok)

	parentID, ok := data1["id"].(string)
	assert.True(t, ok)

	body := "foo"
	res2, _ := upload(t, "/files/"+parentID+"?Type=io.cozy.files&Name=goodhash", "text/plain", body, "rL0Y20zC+Fzt72VPzMSk2A==")
	assert.Equal(t, 201, res2.StatusCode)

	storage, _ := instance.GetStorageProvider()
	buf, err := afero.ReadFile(storage, "/fileparent/goodhash")
	assert.NoError(t, err)
	assert.Equal(t, body, string(buf))
}

func TestUploadAtRootAlreadyExists(t *testing.T) {
	body := "foo"
	res1, _ := upload(t, "/files/?Type=io.cozy.files&Name=iexistfile", "text/plain", body, "rL0Y20zC+Fzt72VPzMSk2A==")
	assert.Equal(t, 201, res1.StatusCode)

	res2, _ := upload(t, "/files/?Type=io.cozy.files&Name=iexistfile", "text/plain", body, "rL0Y20zC+Fzt72VPzMSk2A==")
	assert.Equal(t, 409, res2.StatusCode)
}

func TestUploadWithParentAlreadyExists(t *testing.T) {
	_, dirdata := createDir(t, "/files/?Type=io.cozy.folders&Name=container")

	var ok bool
	dirdata, ok = dirdata["data"].(map[string]interface{})
	assert.True(t, ok)

	parentID, ok := dirdata["id"].(string)
	assert.True(t, ok)

	body := "foo"
	res1, _ := upload(t, "/files/"+parentID+"?Type=io.cozy.files&Name=iexistfile", "text/plain", body, "rL0Y20zC+Fzt72VPzMSk2A==")
	assert.Equal(t, 201, res1.StatusCode)

	res2, _ := upload(t, "/files/"+parentID+"?Type=io.cozy.files&Name=iexistfile", "text/plain", body, "rL0Y20zC+Fzt72VPzMSk2A==")
	assert.Equal(t, 409, res2.StatusCode)
}

func TestModifyMetadataFileMove(t *testing.T) {
	body := "foo"
	res1, data1 := upload(t, "/files/?Type=io.cozy.files&Name=filemoveme&Tags=foo,bar", "text/plain", body, "rL0Y20zC+Fzt72VPzMSk2A==")
	assert.Equal(t, 201, res1.StatusCode)

	var ok bool
	data1, ok = data1["data"].(map[string]interface{})
	assert.True(t, ok)

	fileID, ok := data1["id"].(string)
	assert.True(t, ok)

	res2, data2 := createDir(t, "/files/?Name=movemeinme&Type=io.cozy.folders")
	assert.Equal(t, 201, res2.StatusCode)

	data2, ok = data2["data"].(map[string]interface{})
	assert.True(t, ok)

	folderID, ok := data2["id"].(string)
	assert.True(t, ok)

	attrs := map[string]interface{}{
		"tags":       []string{"bar", "bar", "baz"},
		"name":       "moved",
		"folder_id":  folderID,
		"executable": true,
	}

	res3, data3 := patchFile(t, "/files/"+fileID, "io.cozy.files", fileID, attrs, nil)
	assert.Equal(t, 200, res3.StatusCode)

	data3, ok = data3["data"].(map[string]interface{})
	assert.True(t, ok)

	attrs3, ok := data3["attributes"].(map[string]interface{})
	assert.True(t, ok)

	assert.Equal(t, "text/plain", attrs3["mime"])
	assert.Equal(t, "moved", attrs3["name"])
	assert.EqualValues(t, []interface{}{"bar", "baz"}, attrs3["tags"])
	assert.Equal(t, "text", attrs3["class"])
	assert.Equal(t, "rL0Y20zC+Fzt72VPzMSk2A==", attrs3["md5sum"])
	assert.Equal(t, true, attrs3["executable"])
	assert.Equal(t, "3", attrs3["size"])
}

func TestModifyMetadataFileConflict(t *testing.T) {
	body := "foo"
	res1, data1 := upload(t, "/files/?Type=io.cozy.files&Name=fmodme1&Tags=foo,bar", "text/plain", body, "rL0Y20zC+Fzt72VPzMSk2A==")
	assert.Equal(t, 201, res1.StatusCode)

	res2, _ := upload(t, "/files/?Type=io.cozy.files&Name=fmodme2&Tags=foo,bar", "text/plain", body, "rL0Y20zC+Fzt72VPzMSk2A==")
	assert.Equal(t, 201, res2.StatusCode)

	file1ID, _ := extractDirData(t, data1)

	attrs := map[string]interface{}{
		"name": "fmodme2",
	}

	res3, _ := patchFile(t, "/files/"+file1ID, "io.cozy.files", file1ID, attrs, nil)
	assert.Equal(t, 409, res3.StatusCode)
}

func TestModifyMetadataDirMove(t *testing.T) {
	res1, data1 := createDir(t, "/files/?Name=dirmodme&Type=io.cozy.folders&Tags=foo,bar,bar")
	assert.Equal(t, 201, res1.StatusCode)

	folder1ID, _ := extractDirData(t, data1)

	reschild1, _ := createDir(t, "/files/"+folder1ID+"?Name=child1&Type=io.cozy.folders")
	assert.Equal(t, 201, reschild1.StatusCode)

	reschild2, _ := createDir(t, "/files/"+folder1ID+"?Name=child2&Type=io.cozy.folders")
	assert.Equal(t, 201, reschild2.StatusCode)

	res2, data2 := createDir(t, "/files/?Name=dirmodmemoveinme&Type=io.cozy.folders")
	assert.Equal(t, 201, res2.StatusCode)

	folder2ID, _ := extractDirData(t, data2)

	attrs1 := map[string]interface{}{
		"tags":      []string{"bar", "baz"},
		"name":      "renamed",
		"folder_id": folder2ID,
	}

	res3, _ := patchFile(t, "/files/"+folder1ID, "io.cozy.folders", folder1ID, attrs1, nil)
	assert.Equal(t, 200, res3.StatusCode)

	storage, _ := instance.GetStorageProvider()
	exists, err := afero.DirExists(storage, "/dirmodmemoveinme/renamed")
	assert.NoError(t, err)
	assert.True(t, exists)

	attrs2 := map[string]interface{}{
		"tags":      []string{"bar", "baz"},
		"name":      "renamed",
		"folder_id": folder1ID,
	}

	res4, _ := patchFile(t, "/files/"+folder2ID, "io.cozy.folders", folder2ID, attrs2, nil)
	assert.Equal(t, 412, res4.StatusCode)

	res5, _ := patchFile(t, "/files/"+folder1ID, "io.cozy.folders", folder1ID, attrs2, nil)
	assert.Equal(t, 412, res5.StatusCode)
}

func TestModifyMetadataDirMoveWithRel(t *testing.T) {
	res1, data1 := createDir(t, "/files/?Name=dirmodmewithrel&Type=io.cozy.folders&Tags=foo,bar,bar")
	assert.Equal(t, 201, res1.StatusCode)

	folder1ID, _ := extractDirData(t, data1)

	reschild1, datachild1 := createDir(t, "/files/"+folder1ID+"?Name=child1&Type=io.cozy.folders")
	assert.Equal(t, 201, reschild1.StatusCode)

	reschild2, datachild2 := createDir(t, "/files/"+folder1ID+"?Name=child2&Type=io.cozy.folders")
	assert.Equal(t, 201, reschild2.StatusCode)

	res2, data2 := createDir(t, "/files/?Name=dirmodmemoveinmewithrel&Type=io.cozy.folders")
	assert.Equal(t, 201, res2.StatusCode)

	folder2ID, _ := extractDirData(t, data2)
	child1ID, _ := extractDirData(t, datachild1)
	child2ID, _ := extractDirData(t, datachild2)

	fmt.Println(child1ID, child2ID)

	parent := &jsonData{
		ID:   folder2ID,
		Type: "io.cozy.files",
	}

	res3, _ := patchFile(t, "/files/"+folder1ID, "io.cozy.folders", folder1ID, nil, parent)
	assert.Equal(t, 200, res3.StatusCode)

	storage, _ := instance.GetStorageProvider()
	exists, err := afero.DirExists(storage, "/dirmodmemoveinmewithrel/dirmodmewithrel")
	assert.NoError(t, err)
	assert.True(t, exists)
}

func TestModifyMetadataDirMoveConflict(t *testing.T) {
	res1, _ := createDir(t, "/files/?Name=conflictmodme1&Type=io.cozy.folders&Tags=foo,bar,bar")
	assert.Equal(t, 201, res1.StatusCode)

	res2, data2 := createDir(t, "/files/?Name=conflictmodme2&Type=io.cozy.folders")
	assert.Equal(t, 201, res2.StatusCode)

	folder2ID, _ := extractDirData(t, data2)

	attrs1 := map[string]interface{}{
		"tags": []string{"bar", "baz"},
		"name": "conflictmodme1",
	}

	res3, _ := patchFile(t, "/files/"+folder2ID, "io.cozy.folders", folder2ID, attrs1, nil)
	assert.Equal(t, 409, res3.StatusCode)
}

func TestModifyContentNoFileID(t *testing.T) {
	res, _ := uploadMod(t, "/files/badid", "text/plain", "nil", "")
	assert.Equal(t, 404, res.StatusCode)
}

{'fraction_non_alphanumeric': 0.18829956434548095, 'fraction_numerical': 0.02510199847866676, 'mean_word_length': 4.789231385108087, 'pattern_counts': {'":': 30, '<': 27, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '887271', 'n_tokens_mistral': 12345, 'n_tokens_neox': 11706, 'n_words': 2106}