initial commit

This commit is contained in:
mitch 2022-03-01 11:55:49 -05:00
commit 7e8842c630
17 changed files with 611 additions and 0 deletions

8
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="Go" enabled="true" />
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/loginToTemplate.iml" filepath="$PROJECT_DIR$/.idea/loginToTemplate.iml" />
</modules>
</component>
</project>

73
file_system_store.go Normal file
View File

@ -0,0 +1,73 @@
package main
import (
"encoding/json"
"fmt"
"os"
"reflect"
)
type FileSystemUserStore struct {
database *json.Encoder
userList UserList
}
func NewFileSystemUserStore(file *os.File) (*FileSystemUserStore, error) {
err := initialiseUserDBFile(file)
if err != nil {
fmt.Errorf("problem initializing user db file, %v", err)
}
userList, err := NewUserList(file)
if err != nil {
return nil, fmt.Errorf("problem loading user list from file %s, %v", file.Name(), err)
}
return &FileSystemUserStore{
database: json.NewEncoder(&Tape{file}),
userList: userList,
}, nil
}
func initialiseUserDBFile(file *os.File) error {
file.Seek(0, 0)
info, err := file.Stat()
if err != nil {
return fmt.Errorf("problem getting file info from file %s, %v", file.Name(), err)
}
if info.Size() == 0 {
file.Write([]byte("[]"))
file.Seek(0, 0)
}
return nil
}
// todo add interface
func (f *FileSystemUserStore) AddUserToUserList(user User) {
f.userList = append(f.userList, user)
}
func (f *FileSystemUserStore) DeleteUserFromUserList(user User) {
var userList []User
for _, u := range f.userList {
if !reflect.DeepEqual(u, user) {
userList = append(userList, u)
}
}
f.userList = userList
}
func (f *FileSystemUserStore) GetUsers() UserList {
return f.userList
}
func (f *FileSystemUserStore) UpdateUserFromUserList(toUpdate, newState User) {
user := f.userList.Find(toUpdate.Username)
*user = newState
}

139
file_system_store_test.go Normal file
View File

@ -0,0 +1,139 @@
package main
import (
"io/ioutil"
"os"
"testing"
)
func createTempFile(t testing.TB, initialDB string) (*os.File, func()) {
t.Helper()
tempfile, err := ioutil.TempFile("", "db")
if err != nil {
t.Fatalf("could not create temp file %v", err)
}
tempfile.Write([]byte(initialDB))
removeFile := func() {
tempfile.Close()
os.Remove(tempfile.Name())
}
return tempfile, removeFile
}
func TestFileSystemStore(t *testing.T) {
t.Run("get username", func(t *testing.T) {
database, cleanDatabase := createTempFile(t, `[
{"Username": "testUser", "Key": "12345", "IpAddress": "123.123.123.123.50120", "IsAdmin": true},
{"Username": "jimmy", "Key": "67890", "IpAddress": "12.12.12.12:50121", "IsAdmin": false}]`)
defer cleanDatabase()
store, err := NewFileSystemUserStore(database)
//userList := store.userList
//user := userList.Find("testUser")
//user.UpdateAdminStatus(true)
AssertNoError(t, err)
got := store.GetUsers()
want := []User{
{Username: "testUser", Key: "12345", IpAddress: "123.123.123.123.50120", IsAdmin: true},
{Username: "jimmy", Key: "67890", IpAddress: "12.12.12.12:50121", IsAdmin: false},
}
AssertUsers(t, got, want)
//read again
got = store.GetUsers()
AssertUsers(t, got, want)
})
}
func TestCreateUser(t *testing.T) {
t.Run("get username", func(t *testing.T) {
database, cleanDatabase := createTempFile(t, `[
{"Username": "testUser", "Key": "12345", "IpAddress": "123.123.123.123.50120", "IsAdmin": true},
{"Username": "jimmy", "Key": "67890", "IpAddress": "12.12.12.12:50121", "IsAdmin": false}]`)
defer cleanDatabase()
store, err := NewFileSystemUserStore(database)
AssertNoError(t, err)
want := User{
Username: "newUser",
Key: "12345",
IpAddress: "1.1.1.1:1111",
IsAdmin: false,
}
store.AddUserToUserList(want)
got := store.GetUsers()
AssertUserExists(t, got, want)
})
}
func TestDeleteUser(t *testing.T) {
t.Run("get username", func(t *testing.T) {
database, cleanDatabase := createTempFile(t, `[
{"Username": "testUser", "Key": "12345", "IpAddress": "123.123.123.123.50120", "IsAdmin": true},
{"Username": "jimmy", "Key": "67890", "IpAddress": "12.12.12.12:50121", "IsAdmin": false}]`)
defer cleanDatabase()
store, err := NewFileSystemUserStore(database)
AssertNoError(t, err)
want := User{
Username: "testUser",
Key: "12345",
IpAddress: "123.123.123.123:50120",
IsAdmin: false,
}
want = *store.userList.Find("testUser")
store.DeleteUserFromUserList(want)
got := store.GetUsers()
AssertUserNotExists(t, got, want)
want = *store.userList.Find("jimmy")
AssertUserExists(t, got, want)
})
}
//todo move this to user_test.go and user.go
func TestUpdateUserStore(t *testing.T) {
t.Run("get username", func(t *testing.T) {
database, cleanDatabase := createTempFile(t, `[
{"Username": "testUser", "Key": "12345", "IpAddress": "123.123.123.123.50120", "IsAdmin": true},
{"Username": "jimmy", "Key": "67890", "IpAddress": "12.12.12.12:50121", "IsAdmin": false}]`)
defer cleanDatabase()
store, err := NewFileSystemUserStore(database)
AssertNoError(t, err)
userToUpdate := *store.userList.Find("testUser")
want := User{
Username: "testUser2",
Key: "123456",
IpAddress: "124.124.124.124:50120",
IsAdmin: false,
}
want = *store.userList.Find("testUser")
store.UpdateUserFromUserList(userToUpdate, want)
got := store.GetUsers()
AssertUserExists(t, got, want)
})
}

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module loginToTemplate
go 1.17

17
in_memory_user_store.go Normal file
View File

@ -0,0 +1,17 @@
package main
func NewInMemoryUserStore() *InMemoryUserStore {
return &InMemoryUserStore{map[string]string{}}
}
type InMemoryUserStore struct {
store map[string]string
}
func (i InMemoryUserStore) GetUserName(name string) string {
return name
}
func (i InMemoryUserStore) ShowIP(name string) string {
return i.store[name]
}

16
main.go Normal file
View File

@ -0,0 +1,16 @@
package main
import (
"log"
"net/http"
)
const hostAndPort = ":5000"
func main() {
server := NewLoginServer(NewInMemoryUserStore())
if err := http.ListenAndServe(hostAndPort, server); err != nil {
log.Fatalf("could not listen on port %v", err)
}
}

57
server.go Normal file
View File

@ -0,0 +1,57 @@
package main
import (
"net/http"
)
type UserStore interface {
GetUserName(name string) string
ShowIP(name string) string
}
type ServerHandler struct {
store UserStore
http.Handler
userIpMap map[string]string
}
func NewLoginServer(store UserStore) *ServerHandler {
s := &ServerHandler{
store,
http.NewServeMux(),
nil,
}
router := http.NewServeMux()
router.Handle("/", http.HandlerFunc(s.homeHandler))
s.Handler = router
return s
}
func (s *ServerHandler) homeHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodPost:
s.updateIpHandler(w, r)
case http.MethodGet:
s.sendHomeHandler(w, r)
}
}
func (s *ServerHandler) sendHomeHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(r.RemoteAddr))
}
func (s *ServerHandler) updateIpHandler(w http.ResponseWriter, r *http.Request) {
//userName := strings.TrimPrefix(r.URL.Path, "/")
//if ! s.store.GetUserName(userName)
w.WriteHeader(http.StatusAccepted)
w.Write([]byte(r.RemoteAddr))
}
func (s *ServerHandler) GetUserName(name string) string {
return name
}
func (s *ServerHandler) ShowIP(name string) string {
return s.userIpMap[name]
}

View File

@ -0,0 +1,11 @@
package main
//func TestPortalLoads(t *testing.T) {
// t.Run("Test Portal Loads", func(t *testing.T) {
// server := NewServer()
//
// servr.ServeHTTP(httptest.NewRecorder(), request)
//
// t.Run("")
// })
//}

48
server_test.go Normal file
View File

@ -0,0 +1,48 @@
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"testing"
)
const jsonContentType = "application/json"
func TestPortal(t *testing.T) {
store := StubUserStore{
map[string]string{
"testUser": "123.123.123.123:50120",
"jimmy": "12.12.12.12:50121",
},
}
server := NewLoginServer(&store)
t.Run("Logs client ip", func(t *testing.T) {
for _, want := range store.userIpMap {
t.Run(fmt.Sprintf("got ip from "+want), func(t *testing.T) {
request := NewGetHomeRequest()
request.RemoteAddr = want
response := httptest.NewRecorder()
server.ServeHTTP(response, request)
got := response.Body.String()
AssertStatus(t, response.Code, http.StatusOK)
//todo we should update on post not get
AssertResponseBody(t, got, want)
//t.Fatalf("This test is bad, we should store on post not get")
})
}
})
t.Run("Update client IP", func(t *testing.T) {
request := NewPutHomeRequest("testUser")
want := "5.5.5.5:10121"
request.RemoteAddr = want
response := httptest.NewRecorder()
server.ServeHTTP(response, request)
//todo make this fail
response.Body.String()
AssertStatus(t, response.Code, http.StatusAccepted)
})
}

14
tape.go Normal file
View File

@ -0,0 +1,14 @@
package main
import "os"
// Tape represents an os.File that will re-write from the start on every Write call.
type Tape struct {
File *os.File
}
func (t *Tape) Write(p []byte) (n int, err error) {
t.File.Truncate(0)
t.File.Seek(0, 0)
return t.File.Write(p)
}

25
tape_test.go Normal file
View File

@ -0,0 +1,25 @@
package main
import (
"io/ioutil"
"testing"
)
func Test_Tape_Write(t *testing.T) {
file, clean := createTempFile(t, "12345")
defer clean()
tape := Tape{File: file}
tape.Write([]byte("foo"))
file.Seek(0, 0)
contents, _ := ioutil.ReadAll(file)
got := string(contents)
want := "foo"
if got != want {
t.Errorf("got %v want %v", got, want)
}
}

81
testing.go Normal file
View File

@ -0,0 +1,81 @@
package main
import (
"fmt"
"net/http"
"reflect"
"testing"
)
type StubUserStore struct {
userIpMap map[string]string
}
func (s *StubUserStore) GetUserName(name string) string {
return name
}
func (s *StubUserStore) ShowIP(name string) string {
return s.userIpMap[name]
}
func AssertNoError(t testing.TB, err error) {
t.Helper()
if err != nil {
t.Fatalf("didn't expect an error, but got one, %v", err)
}
}
func NewGetHomeRequest() *http.Request {
req, _ := http.NewRequest(http.MethodGet, fmt.Sprintf("/"), nil)
return req
}
func NewPutHomeRequest(userName string) *http.Request {
req, _ := http.NewRequest(http.MethodPost, fmt.Sprintf("/"+userName), nil)
return req
}
func userInUserList(ul UserList, user User) bool {
for _, u := range ul {
if reflect.DeepEqual(u, user) {
return true
}
}
return false
}
func AssertResponseBody(t testing.TB, got, want string) {
t.Helper()
if got != want {
t.Errorf("response body is wrong, got %q want %q", got, want)
}
}
func AssertStatus(t testing.TB, got, want int) {
t.Helper()
if got != want {
t.Errorf("did not get correct status, expected %d got %d", got, want)
}
}
func AssertUsers(t testing.TB, got, want []User) {
t.Helper()
if !reflect.DeepEqual(got, want) {
t.Errorf("got %v want %v", got, want)
}
}
func AssertUserExists(t testing.TB, got []User, want User) {
t.Helper()
if !userInUserList(got, want) {
t.Errorf("got %v want %v", got, want)
}
}
func AssertUserNotExists(t testing.TB, got []User, want User) {
t.Helper()
if userInUserList(got, want) {
t.Errorf("Expected %v to be deleted but it was not. List is %v", want, got)
}
}

20
user.go Normal file
View File

@ -0,0 +1,20 @@
package main
type User struct {
Username string
Key string
IpAddress string
IsAdmin bool
}
func (u *User) UpdateUserKey(key string) {
u.Key = key
}
func (u *User) UpdateUserIp(ipAddress string) {
u.IpAddress = ipAddress
}
func (u *User) UpdateAdminStatus(isAdmin bool) {
u.IsAdmin = isAdmin
}

31
user_list.go Normal file
View File

@ -0,0 +1,31 @@
package main
import (
"encoding/json"
"fmt"
"io"
)
// UserList stores a collection of users.
type UserList []User
// Find tries to return username from UserList
func (ul UserList) Find(username string) *User {
for i, u := range ul {
if u.Username == username {
return &ul[i]
}
}
return nil
}
// NewUserList creates a UserList from JSON
func NewUserList(rdr io.Reader) (UserList, error) {
var userList []User
err := json.NewDecoder(rdr).Decode(&userList)
if err != nil {
err = fmt.Errorf("problem parsing userlist, %v", err)
}
return userList, err
}

51
user_test.go Normal file
View File

@ -0,0 +1,51 @@
package main
import "testing"
func TestUpdateUserKey(t *testing.T) {
user := User{
Username: "bob",
Key: "12345",
IpAddress: "12.12.12.12:50123",
IsAdmin: false,
}
want := "67890"
user.UpdateUserKey(want)
if user.Key != want {
t.Errorf("Expected key %v, got %v", want, user.Key)
}
}
func TestUpdateUserIp(t *testing.T) {
user := User{
Username: "bob",
Key: "12345",
IpAddress: "12.12.12.12:50123",
IsAdmin: false,
}
want := "13.13.13.13:50124"
user.UpdateUserIp(want)
if user.IpAddress != want {
t.Errorf("Expected key %v, got %v", want, user.IpAddress)
}
}
func TestAddAdmin(t *testing.T) {
user := User{
Username: "bob",
Key: "12345",
IpAddress: "12.12.12.12:50123",
IsAdmin: false,
}
want := true
user.UpdateAdminStatus(want)
if user.IsAdmin != want {
t.Errorf("Expected key %v, got %v", want, user.IsAdmin)
}
}