// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License.  You may obtain a copy
// of the License at:
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
// License for the specific language governing permissions and limitations
// under the License.

package integration

import (
	"io/ioutil"
	"os"
	"runtime"
	"testing"

	"github.com/bazelbuild/sandboxfs/integration/utils"
)

func TestOptions_Allow(t *testing.T) {
	root := utils.RequireRoot(t, "Requires root privileges to spawn sandboxfs under different users")

	user := utils.GetConfig().UnprivilegedUser
	if user == nil {
		t.Skipf("unprivileged user not set; must contain the name of an unprivileged user with FUSE access")
	}
	t.Logf("Using primary unprivileged user: %v", user)

	other, err := utils.LookupUserOtherThan(root.Username, user.Username)
	if err != nil {
		t.Fatal(err)
	}
	t.Logf("Using secondary unprivileged user: %v", other)

	var allowRootWorks bool
	switch runtime.GOOS {
	case "darwin":
		allowRootWorks = true
	case "linux":
		allowRootWorks = false
	default:
		t.Fatalf("Don't know how this test behaves in this platform")
	}

	testData := []struct {
		name string

		allowFlag   string
		wantMountOk bool
		okUsers     []*utils.UnixUser
		notOkUsers  []*utils.UnixUser
	}{
		{"Default", "", true, []*utils.UnixUser{user}, []*utils.UnixUser{root, other}},
		{"Other", "--allow=other", true, []*utils.UnixUser{user, other, root}, []*utils.UnixUser{}},
		{"Root", "--allow=root", allowRootWorks, []*utils.UnixUser{user, root}, []*utils.UnixUser{other}},
		{"Self", "--allow=self", true, []*utils.UnixUser{user}, []*utils.UnixUser{root, other}},
	}
	for _, d := range testData {
		t.Run(d.name, func(t *testing.T) {
			tempDir, err := ioutil.TempDir("", "test")
			if err != nil {
				t.Fatalf("Failed to create temporary directory: %v", err)
			}
			defer os.RemoveAll(tempDir)

			if !d.wantMountOk {
				_, stderr, err := utils.RunAndWait(1, d.allowFlag, "--mapping=ro:/:/", tempDir)
				if err != nil {
					t.Fatal(err)
				}
				if !utils.MatchesRegexp("known.*broken", stderr) {
					t.Errorf("Want error message to mention known brokenness; got %v", stderr)
				}
				return
			}

			args := []string{"--mapping=ro:/:%ROOT%"}
			if d.allowFlag != "" {
				args = append(args, d.allowFlag)
			}

			state := utils.MountSetupWithUser(t, user, args...)
			defer state.TearDown(t)

			utils.MustWriteFile(t, state.RootPath("file"), 0444, "")
			file := state.MountPath("file")

			for _, user := range d.okUsers {
				if err := utils.FileExistsAsUser(file, user); err != nil {
					t.Errorf("Failed to access mount point as user %s: %v", user.Username, err)
				}
			}

			for _, user := range d.notOkUsers {
				if err := utils.FileExistsAsUser(file, user); err == nil {
					t.Errorf("Was able to access mount point as user %s; want error", user.Username)
				}
			}
		})
	}
}

func TestOptions_Syntax(t *testing.T) {
	testData := []struct {
		name string

		args       []string
		wantStderr string
	}{
		{"AllowBadValue", []string{"--allow=foo"}, "foo.*must be one of.*other"},
	}
	for _, d := range testData {
		t.Run(d.name, func(t *testing.T) {
			stdout, stderr, err := utils.RunAndWait(2, d.args...)
			if err != nil {
				t.Fatal(err)
			}
			if len(stdout) > 0 {
				t.Errorf("Got %s; want stdout to be empty", stdout)
			}
			if !utils.MatchesRegexp(d.wantStderr, stderr) {
				t.Errorf("Got %s; want stderr to match %s", stderr, d.wantStderr)
			}
			if !utils.MatchesRegexp("--help", stderr) {
				t.Errorf("Got %s; want --help mention in stderr", stderr)
			}
		})
	}
}
