from origin repo gh:ddworken/hishtory, commit 480630e9181167b51554f4407db55717d9b7e4dd
This commit is contained in:
87
shared/data.go
Normal file
87
shared/data.go
Normal file
@@ -0,0 +1,87 @@
|
||||
package shared
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
type EncHistoryEntry struct {
|
||||
EncryptedData []byte `json:"enc_data"`
|
||||
Nonce []byte `json:"nonce"`
|
||||
DeviceId string `json:"device_id"`
|
||||
UserId string `json:"user_id"`
|
||||
Date time.Time `json:"time"`
|
||||
EncryptedId string `json:"id"`
|
||||
ReadCount int `json:"read_count"`
|
||||
}
|
||||
|
||||
/*
|
||||
Manually created the indices:
|
||||
CREATE INDEX CONCURRENTLY device_id_idx ON enc_history_entries USING btree(device_id);
|
||||
CREATE INDEX CONCURRENTLY read_count_idx ON enc_history_entries USING btree(read_count);
|
||||
CREATE INDEX CONCURRENTLY redact_idx ON enc_history_entries USING btree(user_id, device_id, date);
|
||||
*/
|
||||
|
||||
type Device struct {
|
||||
UserId string `json:"user_id"`
|
||||
DeviceId string `json:"device_id"`
|
||||
// The IP address that was used to register the device. Recorded so
|
||||
// that I can count how many people are using hishtory and roughly
|
||||
// from where. If you would like this deleted, please email me at
|
||||
// david@daviddworken.com and I can clear it from your device entries.
|
||||
RegistrationIp string `json:"registration_ip"`
|
||||
RegistrationDate time.Time `json:"registration_date"`
|
||||
}
|
||||
|
||||
type DumpRequest struct {
|
||||
UserId string `json:"user_id"`
|
||||
RequestingDeviceId string `json:"requesting_device_id"`
|
||||
RequestTime time.Time `json:"request_time"`
|
||||
}
|
||||
|
||||
type UpdateInfo struct {
|
||||
LinuxAmd64Url string `json:"linux_amd_64_url"`
|
||||
LinuxAmd64AttestationUrl string `json:"linux_amd_64_attestation_url"`
|
||||
DarwinAmd64Url string `json:"darwin_amd_64_url"`
|
||||
DarwinAmd64UnsignedUrl string `json:"darwin_amd_64_unsigned_url"`
|
||||
DarwinAmd64AttestationUrl string `json:"darwin_amd_64_attestation_url"`
|
||||
DarwinArm64Url string `json:"darwin_arm_64_url"`
|
||||
DarwinArm64UnsignedUrl string `json:"darwin_arm_64_unsigned_url"`
|
||||
DarwinArm64AttestationUrl string `json:"darwin_arm_64_attestation_url"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
type DeletionRequest struct {
|
||||
UserId string `json:"user_id"`
|
||||
DestinationDeviceId string `json:"destination_device_id"`
|
||||
SendTime time.Time `json:"send_time"`
|
||||
Messages MessageIdentifiers `json:"messages"`
|
||||
ReadCount int `json:"read_count"`
|
||||
}
|
||||
|
||||
type MessageIdentifiers struct {
|
||||
Ids []MessageIdentifier `json:"message_ids"`
|
||||
}
|
||||
|
||||
type MessageIdentifier struct {
|
||||
DeviceId string `json:"device_id"`
|
||||
Date time.Time `json:"date"`
|
||||
}
|
||||
|
||||
func (m *MessageIdentifiers) Scan(value interface{}) error {
|
||||
bytes, ok := value.([]byte)
|
||||
if !ok {
|
||||
return fmt.Errorf("failed to unmarshal JSONB value: %v", value)
|
||||
}
|
||||
|
||||
result := MessageIdentifiers{}
|
||||
err := json.Unmarshal(bytes, &result)
|
||||
*m = result
|
||||
return err
|
||||
}
|
||||
|
||||
func (m MessageIdentifiers) Value() (driver.Value, error) {
|
||||
return json.Marshal(m)
|
||||
}
|
283
shared/testutils/testutils.go
Normal file
283
shared/testutils/testutils.go
Normal file
@@ -0,0 +1,283 @@
|
||||
package testutils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ddworken/hishtory/client/data"
|
||||
)
|
||||
|
||||
const (
|
||||
DB_WAL_PATH = data.DB_PATH + "-wal"
|
||||
DB_SHM_PATH = data.DB_PATH + "-shm"
|
||||
)
|
||||
|
||||
func ResetLocalState(t *testing.T) {
|
||||
homedir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to retrieve homedir: %v", err)
|
||||
}
|
||||
|
||||
_ = BackupAndRestoreWithId(t, "-reset-local-state")
|
||||
_ = os.RemoveAll(path.Join(homedir, data.HISHTORY_PATH))
|
||||
}
|
||||
|
||||
func BackupAndRestore(t *testing.T) func() {
|
||||
return BackupAndRestoreWithId(t, "")
|
||||
}
|
||||
|
||||
func getBackPath(file, id string) string {
|
||||
if strings.Contains(file, "/"+data.HISHTORY_PATH+"/") {
|
||||
return strings.Replace(file, data.HISHTORY_PATH, data.HISHTORY_PATH+".test", 1) + id
|
||||
}
|
||||
return file + ".bak" + id
|
||||
}
|
||||
|
||||
func BackupAndRestoreWithId(t *testing.T, id string) func() {
|
||||
ResetFakeHistoryTimestamp()
|
||||
homedir, err := os.UserHomeDir()
|
||||
Check(t, err)
|
||||
initialWd, err := os.Getwd()
|
||||
Check(t, err)
|
||||
Check(t, os.MkdirAll(path.Join(homedir, data.HISHTORY_PATH+".test"), os.ModePerm))
|
||||
|
||||
renameFiles := []string{
|
||||
path.Join(homedir, data.HISHTORY_PATH, data.DB_PATH),
|
||||
path.Join(homedir, data.HISHTORY_PATH, DB_WAL_PATH),
|
||||
path.Join(homedir, data.HISHTORY_PATH, DB_SHM_PATH),
|
||||
path.Join(homedir, data.HISHTORY_PATH, data.CONFIG_PATH),
|
||||
path.Join(homedir, data.HISHTORY_PATH, "hishtory"),
|
||||
path.Join(homedir, data.HISHTORY_PATH, "config.sh"),
|
||||
path.Join(homedir, data.HISHTORY_PATH, "config.zsh"),
|
||||
path.Join(homedir, data.HISHTORY_PATH, "config.fish"),
|
||||
path.Join(homedir, ".bash_history"),
|
||||
path.Join(homedir, ".zsh_history"),
|
||||
path.Join(homedir, ".local/share/fish/fish_history"),
|
||||
}
|
||||
for _, file := range renameFiles {
|
||||
touchFile(file)
|
||||
_ = os.Rename(file, getBackPath(file, id))
|
||||
}
|
||||
copyFiles := []string{
|
||||
path.Join(homedir, ".zshrc"),
|
||||
path.Join(homedir, ".bashrc"),
|
||||
path.Join(homedir, ".bash_profile"),
|
||||
}
|
||||
for _, file := range copyFiles {
|
||||
touchFile(file)
|
||||
_ = copy(file, getBackPath(file, id))
|
||||
}
|
||||
configureZshrc(homedir)
|
||||
touchFile(path.Join(homedir, ".bash_history"))
|
||||
touchFile(path.Join(homedir, ".zsh_history"))
|
||||
touchFile(path.Join(homedir, ".local/share/fish/fish_history"))
|
||||
return func() {
|
||||
Check(t, os.MkdirAll(path.Join(homedir, data.HISHTORY_PATH), os.ModePerm))
|
||||
for _, file := range renameFiles {
|
||||
checkError(os.Rename(getBackPath(file, id), file))
|
||||
}
|
||||
for _, file := range copyFiles {
|
||||
checkError(copy(getBackPath(file, id), file))
|
||||
}
|
||||
if runtime.GOOS != "windows" {
|
||||
cmd := exec.Command("killall", "hishtory")
|
||||
stdout, err := cmd.Output()
|
||||
if err != nil && err.Error() != "exit status 1" {
|
||||
t.Fatalf("failed to execute killall hishtory, stdout=%#v: %v", string(stdout), err)
|
||||
}
|
||||
}
|
||||
checkError(os.Chdir(initialWd))
|
||||
}
|
||||
}
|
||||
|
||||
func touchFile(p string) {
|
||||
_, err := os.Stat(p)
|
||||
if os.IsNotExist(err) {
|
||||
checkError(os.MkdirAll(filepath.Dir(p), os.ModePerm))
|
||||
file, err := os.Create(p)
|
||||
checkError(err)
|
||||
defer file.Close()
|
||||
} else {
|
||||
currentTime := time.Now().Local()
|
||||
err := os.Chtimes(p, currentTime, currentTime)
|
||||
checkError(err)
|
||||
}
|
||||
}
|
||||
|
||||
func configureZshrc(homedir string) {
|
||||
f, err := os.OpenFile(path.Join(homedir, ".zshrc"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
checkError(err)
|
||||
defer f.Close()
|
||||
_, err = f.WriteString(`export HISTFILE=~/.zsh_history
|
||||
export HISTSIZE=10000
|
||||
export SAVEHIST=1000
|
||||
setopt SHARE_HISTORY
|
||||
`)
|
||||
checkError(err)
|
||||
}
|
||||
|
||||
func copy(src, dst string) error {
|
||||
in, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer in.Close()
|
||||
|
||||
out, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
_, err = io.Copy(out, in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return out.Close()
|
||||
}
|
||||
|
||||
func BackupAndRestoreEnv(k string) func() {
|
||||
origValue := os.Getenv(k)
|
||||
return func() {
|
||||
os.Setenv(k, origValue)
|
||||
}
|
||||
}
|
||||
|
||||
func checkError(err error) {
|
||||
if err != nil {
|
||||
_, filename, line, _ := runtime.Caller(1)
|
||||
_, cf, cl, _ := runtime.Caller(2)
|
||||
log.Fatalf("testutils fatal error at %s:%d (caller: %s:%d): %v", filename, line, cf, cl, err)
|
||||
}
|
||||
}
|
||||
|
||||
func buildServer() string {
|
||||
for i := 0; i < 100; i++ {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to getwd: %v", err))
|
||||
}
|
||||
if strings.HasSuffix(wd, "hishtory") {
|
||||
break
|
||||
}
|
||||
err = os.Chdir("../")
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to chdir: %v", err))
|
||||
}
|
||||
if wd == "/" {
|
||||
panic("failed to cd into hishtory dir!")
|
||||
}
|
||||
}
|
||||
version, err := os.ReadFile("VERSION")
|
||||
if err != nil {
|
||||
if runtime.GOOS == "windows" {
|
||||
// TODO: Figure out why it can't read the VERSION file
|
||||
version = []byte("174")
|
||||
} else {
|
||||
panic(fmt.Sprintf("failed to read VERSION file: %v", err))
|
||||
}
|
||||
}
|
||||
f, err := os.CreateTemp("", "server")
|
||||
checkError(err)
|
||||
fn := f.Name()
|
||||
cmd := exec.Command("go", "build", "-o", fn, "-ldflags", fmt.Sprintf("-X main.ReleaseVersion=v0.%s", version), "backend/server/server.go")
|
||||
var stdout bytes.Buffer
|
||||
cmd.Stdout = &stdout
|
||||
var stderr bytes.Buffer
|
||||
cmd.Stderr = &stderr
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to start to build server: %v, stderr=%#v, stdout=%#v", err, stderr.String(), stdout.String()))
|
||||
}
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
wd, _ := os.Getwd()
|
||||
panic(fmt.Sprintf("failed to build server: %v, wd=%#v, stderr=%#v, stdout=%#v", err, wd, stderr.String(), stdout.String()))
|
||||
}
|
||||
return fn
|
||||
}
|
||||
|
||||
func RunTestServer() func() {
|
||||
os.Setenv("HISHTORY_SERVER", "http://localhost:8080")
|
||||
fn := buildServer()
|
||||
cmd := exec.Command(fn)
|
||||
var stdout bytes.Buffer
|
||||
cmd.Stdout = &stdout
|
||||
var stderr bytes.Buffer
|
||||
cmd.Stderr = &stderr
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to start server: %v", err))
|
||||
}
|
||||
time.Sleep(time.Second * 5)
|
||||
go func() {
|
||||
_ = cmd.Wait()
|
||||
}()
|
||||
return func() {
|
||||
err := cmd.Process.Kill()
|
||||
if err != nil && err.Error() != "os: process already finished" {
|
||||
panic(fmt.Sprintf("failed to kill server process: %v", err))
|
||||
}
|
||||
if strings.Contains(stderr.String()+stdout.String(), "failed to") && IsOnline() {
|
||||
panic(fmt.Sprintf("server failed to do something: stderr=%#v, stdout=%#v", stderr.String(), stdout.String()))
|
||||
}
|
||||
if strings.Contains(stderr.String()+stdout.String(), "ERROR:") {
|
||||
panic(fmt.Sprintf("server experienced an error: stderr=%#v, stdout=%#v", stderr.String(), stdout.String()))
|
||||
}
|
||||
// fmt.Printf("stderr=%#v, stdout=%#v\n", stderr.String(), stdout.String())
|
||||
}
|
||||
}
|
||||
|
||||
func Check(t *testing.T, err error) {
|
||||
if err != nil {
|
||||
_, filename, line, _ := runtime.Caller(1)
|
||||
t.Fatalf("Unexpected error at %s:%d: %v", filename, line, err)
|
||||
}
|
||||
}
|
||||
|
||||
func CheckWithInfo(t *testing.T, err error, additionalInfo string) {
|
||||
if err != nil {
|
||||
_, filename, line, _ := runtime.Caller(1)
|
||||
t.Fatalf("Unexpected error: %v at %s:%d! Additional info: %v", err, filename, line, additionalInfo)
|
||||
}
|
||||
}
|
||||
|
||||
func IsOnline() bool {
|
||||
_, err := http.Get("https://hishtory.dev")
|
||||
return err == nil
|
||||
}
|
||||
|
||||
var fakeHistoryTimestamp int64 = 1666068191
|
||||
|
||||
func ResetFakeHistoryTimestamp() {
|
||||
fakeHistoryTimestamp = 1666068191
|
||||
}
|
||||
|
||||
func MakeFakeHistoryEntry(command string) data.HistoryEntry {
|
||||
fakeHistoryTimestamp += 5
|
||||
return data.HistoryEntry{
|
||||
LocalUsername: "david",
|
||||
Hostname: "localhost",
|
||||
Command: command,
|
||||
CurrentWorkingDirectory: "/tmp/",
|
||||
HomeDirectory: "/home/david/",
|
||||
ExitCode: 2,
|
||||
StartTime: time.Unix(fakeHistoryTimestamp, 0),
|
||||
EndTime: time.Unix(fakeHistoryTimestamp+3, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func IsGithubAction() bool {
|
||||
return os.Getenv("GITHUB_ACTION") != ""
|
||||
}
|
Reference in New Issue
Block a user