diff --git a/.gitignore b/.gitignore index fe46aad7..05722bde 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ twitch-cli # Output of the go coverage tool, specifically when used with LiteIDE *.out +*.html # Editor configs .vsvode/ diff --git a/go.mod b/go.mod old mode 100644 new mode 100755 index bc43fa36..c2d5e658 --- a/go.mod +++ b/go.mod @@ -7,7 +7,6 @@ require ( github.com/fatih/color v1.10.0 github.com/hokaccha/go-prettyjson v0.0.0-20201222001619-a42f9ac2ec8e // indirect github.com/jmoiron/sqlx v1.3.3 - github.com/karalabe/xgo v0.0.0-20191115072854-c5ccff8648a7 // indirect github.com/manifoldco/promptui v0.8.0 github.com/mattn/go-sqlite3 v1.14.7 github.com/mitchellh/go-homedir v1.1.0 diff --git a/go.sum b/go.sum index 04d4d78b..1647dd6c 100644 --- a/go.sum +++ b/go.sum @@ -11,6 +11,7 @@ cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqCl cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -26,9 +27,11 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -52,6 +55,7 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -74,6 +78,7 @@ github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OI github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= @@ -109,19 +114,21 @@ github.com/jmoiron/sqlx v1.3.3/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXL github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU= github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/karalabe/xgo v0.0.0-20191115072854-c5ccff8648a7 h1:AYzjK/SHz6m6mg5iuFwkrAhCc14jvCpW9d6frC9iDPE= -github.com/karalabe/xgo v0.0.0-20191115072854-c5ccff8648a7/go.mod h1:iYGcTYIPUvEWhFo6aKUuLchs+AV4ssYdyuBbQJZGcBk= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a h1:weJVJJRzAJBFRlAiJQROKQs8oC9vOxvm4rZmBBk0ONw= github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= @@ -136,8 +143,6 @@ github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-sqlite3 v1.14.5 h1:1IdxlwTNazvbKJQSxoJ5/9ECbEeaTTyeU7sEAZ5KKTQ= -github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.7 h1:fxWBnXkxfM6sRiuH3bqJ4CfzZojMOLVc0UTsTglEghA= github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= @@ -181,7 +186,9 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -200,7 +207,6 @@ github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5q github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -326,6 +332,7 @@ google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiq google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= diff --git a/internal/api/api.go b/internal/api/api.go index c6db85ea..49dc6b2f 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -109,7 +109,6 @@ func NewRequest(method string, path string, queryParameters []string, body []byt break } - // log.Printf("%v", apiResponse) if autopaginate == false { data.Pagination.Cursor = apiResponse.Pagination.Cursor break @@ -123,7 +122,6 @@ func NewRequest(method string, path string, queryParameters []string, body []byt } if data.Data == nil { - log.Println("here") data.Data = make([]interface{}, 0) } // handle json marshalling better; returns empty slice vs. null diff --git a/internal/api/api_request_test.go b/internal/api/api_request_test.go index 62c91ff8..594b461b 100644 --- a/internal/api/api_request_test.go +++ b/internal/api/api_request_test.go @@ -8,11 +8,11 @@ import ( "net/http/httptest" "testing" - "github.com/twitchdev/twitch-cli/internal/util" + "github.com/twitchdev/twitch-cli/test_setup" ) func TestApiRequest(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) var ok = "{\"status\":\"ok\"}" ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/internal/api/api_test.go b/internal/api/api_test.go index d13fb6df..ac37c472 100644 --- a/internal/api/api_test.go +++ b/internal/api/api_test.go @@ -11,6 +11,7 @@ import ( "github.com/spf13/viper" "github.com/twitchdev/twitch-cli/internal/util" + "github.com/twitchdev/twitch-cli/test_setup" ) var params = apiRequestParameters{ @@ -19,7 +20,7 @@ var params = apiRequestParameters{ } func TestNewRequest(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) viper.Set("clientid", "1111") viper.Set("clientsecret", "2222") viper.Set("accesstoken", "4567") @@ -68,7 +69,7 @@ func TestNewRequest(t *testing.T) { } func TestValidOptions(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) get := ValidOptions("GET") a.NotEmpty(get) @@ -78,7 +79,7 @@ func TestValidOptions(t *testing.T) { } func TestGetClientInformation(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) viper.Set("clientid", "1111") viper.Set("clientsecret", "2222") diff --git a/internal/api/client_test.go b/internal/api/client_test.go index f183c7d7..3560a1eb 100644 --- a/internal/api/client_test.go +++ b/internal/api/client_test.go @@ -9,12 +9,12 @@ import ( "testing" "time" - "github.com/twitchdev/twitch-cli/internal/util" + "github.com/twitchdev/twitch-cli/test_setup" "golang.org/x/time/rate" ) func TestNewClient(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) var ok = "{\"status\":\"ok\"}" ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/internal/database/_schema.sql b/internal/database/_schema.sql index 702e594f..b10b2e0c 100644 --- a/internal/database/_schema.sql +++ b/internal/database/_schema.sql @@ -8,15 +8,19 @@ create table categories( id text not null primary key, category_name text not null ); create table users( - id text not null primary key, user_login text not null, - display_name text not null, email text not null, - user_type text, broadcaster_type text, - user_description text, created_at text not null, + id text not null primary key, + user_login text not null, + display_name text not null, + email text not null, + user_type text, + broadcaster_type text, + user_description text, + created_at text not null, category_id text, - modified_at text, + modified_at text, stream_language text not null default 'en', title text not null default '', - delay int not null default 0, + delay int not null default 0, foreign key (category_id) references categories(id) ); create table follows ( @@ -39,21 +43,21 @@ create table bans ( broadcaster_id text not null, user_id text not null, created_at text not null, - expires_at text, + expires_at text, primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table ban_events ( - id text not null primary key, - event_timestamp text not null, - event_type text not null, - event_version text not null default '1.0', - broadcaster_id text not null, - user_id text not null, - expires_at text, - foreign key (broadcaster_id) references users(id), - foreign key (user_id) references users(id) + id text not null primary key, + event_timestamp text not null, + event_type text not null, + event_version text not null default '1.0', + broadcaster_id text not null, + user_id text not null, + expires_at text, + foreign key (broadcaster_id) references users(id), + foreign key (user_id) references users(id) ); create table moderators ( broadcaster_id text not null, @@ -65,7 +69,7 @@ create table moderators ( ); create table moderator_actions ( id text not null primary key, - event_timestamp text not null, + event_timestamp text not null, event_type text not null, event_version text not null default '1.0', broadcaster_id text not null, @@ -108,12 +112,12 @@ create table channel_points_redemptions( id text not null primary key, reward_id text not null, broadcaster_id text not null, - user_id text not null, + user_id text not null, user_input text, redemption_status text not null, redeemed_at text, foreign key (reward_id) references channel_points_rewards(id), - foreign key (broadcaster_id) references users(id), + foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table streams( @@ -160,12 +164,12 @@ create table videos( view_count int not null default 0, duration text not null, video_language text not null default 'en', - category_id text, + category_id text, type text default 'archive', foreign key (stream_id) references streams(id), - foreign key (broadcaster_id) references users(id), - foreign key (category_id) references categories(id) -}; + foreign key (broadcaster_id) references users(id), + foreign key (category_id) references categories(id) + ); create table stream_markers( id text not null primary key, video_id text not null, @@ -189,7 +193,7 @@ create table subscriptions ( is_gift boolean not null default false, gifter_id text, tier text not null default '1000', - created_at text not null, + created_at text not null, primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id), @@ -229,7 +233,7 @@ create table polls ( status text not null, duration int not null, started_at text not null, - ended_at text not null, + ended_at text, foreign key (broadcaster_id) references users(id) ); create table poll_choices ( @@ -242,41 +246,46 @@ create table poll_choices ( foreign key (poll_id) references polls(id) ); create table predictions ( - id text not null, + id text not null primary key, broadcaster_id text not null, title text not null, winning_outcome_id text, prediction_window int, status text not null, created_at text not null, - ended_at text not null, - locked_at text not null, + ended_at text, + locked_at text, foreign key (broadcaster_id) references users(id) ); create table prediction_outcomes ( - id text not null, title text not null, - users int not null default 0, channel_points int not null default 0, - color text not null + id text not null primary key, + title text not null, + users int not null default 0, + channel_points int not null default 0, + color text not null, + prediction_id text not null, + foreign key (prediction_id) references predictions(id) ); create table prediction_predictions ( prediction_id text not null, user_id text not null, amount int not null, - color text not null, + outcome_id text not null, primary key(prediction_id, user_id), foreign key(user_id) references users(id), - foreign key(prediction_id) references predictions(id) + foreign key(prediction_id) references predictions(id), + foreign key(outcome_id) references prediction_outcomes(id) ); create table clips ( id text not null primary key, - broadcaster_id text not null, - creator_id text not null, - video_id text not null, - game_id text not null, - title text not null, + broadcaster_id text not null, + creator_id text not null, + video_id text not null, + game_id text not null, + title text not null, view_count int default 0, - created_at text not null, - duration real not null, - foreign key (broadcaster_id) references users(id), + created_at text not null, + duration real not null, + foreign key (broadcaster_id) references users(id), foreign key (creator_id) references users(id) -); \ No newline at end of file +); diff --git a/internal/database/_template.go b/internal/database/_template.go index 09ed01ce..408bd257 100644 --- a/internal/database/_template.go +++ b/internal/database/_template.go @@ -11,7 +11,6 @@ func (q *Query) GetPrinciple(p Principle) (*DBResponse, error) { var r Principle sql := generateSQL("select * from principle", u, SEP_AND) - sql = fmt.Sprintf("%v LIMIT 1", sql) rows, err := q.DB.NamedQuery(sql, u) if err != nil { return r, err diff --git a/internal/database/authentication.go b/internal/database/authentication.go index 8c908f76..6f0afe64 100644 --- a/internal/database/authentication.go +++ b/internal/database/authentication.go @@ -54,13 +54,10 @@ func (q *Query) InsertOrUpdateAuthenticationClient(client AuthenticationClient, client.Secret = generateString(30) for { - tx := db.MustBegin() - tx.NamedExec(stmt, client) - err := tx.Commit() + _, err := db.NamedExec(generateInsertSQL("clients", "id", client, upsert), client) if err == nil { return client, err } - client.ID = util.RandomClientID() } } diff --git a/internal/database/categories.go b/internal/database/categories.go index 23f6fa35..f2c0de4f 100644 --- a/internal/database/categories.go +++ b/internal/database/categories.go @@ -73,7 +73,7 @@ func (q *Query) SearchCategories(query string) (*DBResponse, error) { func (q *Query) GetTopGames() (*DBResponse, error) { r := []Category{} - err := q.DB.Select(&r, "select c.id, c.category_name, SUM(s.viewer_count) as vc from users u join streams s on s.broadcaster_id = u.id left join categories c on u.category_id = c.id group by c.id, c.category_name order by vc desc") + err := q.DB.Select(&r, "select c.id, c.category_name, IFNULL(SUM(s.viewer_count),0) as vc from categories c left join users u on c.id = u.category_id left join streams s on s.broadcaster_id = u.id group by c.id, c.category_name order by vc desc") if err != nil { return nil, err } diff --git a/internal/database/channel_points_rewards.go b/internal/database/channel_points_rewards.go index de028d22..a7725233 100644 --- a/internal/database/channel_points_rewards.go +++ b/internal/database/channel_points_rewards.go @@ -16,7 +16,7 @@ type ChannelPointsReward struct { BackgroundColor string `db:"background_color" json:"background_color"` IsEnabled *bool `db:"is_enabled" json:"is_enabled"` Cost int `db:"cost" json:"cost"` - Title string `db:"title" json:"title"` + Title string `db:"title" dbs:"cpr.title" json:"title"` RewardPrompt string `db:"reward_prompt" json:"prompt"` IsUserInputRequired bool `db:"is_user_input_required" json:"is_user_input_requird"` MaxPerStream `json:"max_per_stream_setting"` diff --git a/internal/database/database.go b/internal/database/database.go index e1a3da5a..bcb05b4b 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -46,20 +46,26 @@ func getDatabase() (sqlx.DB, error) { needToInit = true } - // open and force Foreign Key support ("fk=true") - db, err := sqlx.Open("sqlite3", path+"?_fk=true") - if err != nil { - return sqlx.DB{}, err - } - - if needToInit == true { - err = initDatabase(*db) + for i := 0; i <= 5; i++ { + // open and force Foreign Key support ("fk=true") + db, err := sqlx.Open("sqlite3", path+"?_fk=true&cache=shared") if err != nil { - log.Print(err) - return sqlx.DB{}, err + if i == 5 { + return sqlx.DB{}, err + } + continue + } + + if needToInit == true { + err = initDatabase(*db) + if err != nil { + log.Print(err) + return sqlx.DB{}, err + } } - } - checkAndUpdate(*db) - return *db, nil + checkAndUpdate(*db) + return *db, nil + } + return sqlx.DB{}, nil } diff --git a/internal/database/database_test.go b/internal/database/database_test.go index 1a830995..aac72871 100644 --- a/internal/database/database_test.go +++ b/internal/database/database_test.go @@ -3,17 +3,26 @@ package database import ( + "database/sql" + "errors" + "fmt" + "net/http" "os" "path/filepath" "testing" "time" + "github.com/mattn/go-sqlite3" "github.com/spf13/viper" "github.com/twitchdev/twitch-cli/internal/util" + "github.com/twitchdev/twitch-cli/test_setup" ) +const TEST_USER_ID = "1" +const TEST_USER_LOGIN = "testing_user1" + func TestGetDatabase(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) p, _ := util.GetApplicationDir() dbFileName = viper.GetString("DB_FILENAME") @@ -39,7 +48,7 @@ func TestGetDatabase(t *testing.T) { } func TestRetriveFromDB(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) db, err := NewConnection() a.Nil(err) @@ -62,7 +71,707 @@ func TestRetriveFromDB(t *testing.T) { dbResponse, err := q.GetEventByID(ecParams.ID) a.Nil(err) - println(dbResponse.ID) a.NotNil(dbResponse) a.Equal("test", dbResponse.Transport) } + +func TestGenerateString(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + s := generateString(10) + + a.Len(s, 10) +} + +func TestAuthentication(t *testing.T) { + a := test_setup.SetupTestEnv(t) + client := AuthenticationClient{ID: "1234", Secret: "4567", Name: "for_testing", IsExtension: false} + db, _ := NewConnection() + q := db.NewQuery(nil, 100) + + // test true insert + ac, err := q.InsertOrUpdateAuthenticationClient(client, false) + a.Nil(err) + a.NotNil(ac) + + // if duped, should give a fresh client ID + ac, err = q.InsertOrUpdateAuthenticationClient(client, false) + a.Nil(err) + a.NotNil(ac) + a.NotEqual(ac.ID, client.ID, fmt.Sprintf("%v %v", ac.ID, client.ID)) + + // test upsert + ac, err = q.InsertOrUpdateAuthenticationClient(client, true) + a.Nil(err) + a.NotNil(ac) + + // create a fake auth + auth, err := q.CreateAuthorization(Authorization{ClientID: ac.ID}) + a.Nil(err) + a.NotNil(auth) + + // test fetching client + dbr, err := q.GetAuthenticationClient(AuthenticationClient{ID: auth.ClientID}) + a.Nil(err) + c := dbr.Data.([]AuthenticationClient) + a.NotNil(c) + a.Len(c, 1) + a.Equal(c[0].ID, client.ID) + + authorization, err := q.GetAuthorizationByToken(auth.Token) + a.Nil(err) + a.Equal(client.ID, authorization.ClientID) +} + +func TestAPI(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + db, _ := NewConnection() + b := db.IsFirstRun() + + a.Equal(b, true) +} + +func TestCategories(t *testing.T) { + a := test_setup.SetupTestEnv(t) + db, err := NewConnection() + a.Nil(err) + + q := db.NewQuery(nil, 100) + + c := Category{Name: "test", ID: "1"} + err = q.InsertCategory(c, false) + a.Nil(err) + + // get categories + dbr, err := q.GetCategories(Category{ID: c.ID}) + a.Nil(err) + categories := dbr.Data.([]Category) + a.Len(categories, 1) + a.Equal(c.ID, categories[0].ID) + + // search + dbr, err = q.SearchCategories("es") + a.Nil(err) + categories = dbr.Data.([]Category) + a.Len(categories, 1) + a.Equal(c.ID, categories[0].ID) + + // top + dbr, err = q.GetTopGames() + a.Nil(err) + categories = dbr.Data.([]Category) + a.Len(categories, 1) + a.Equal(c.ID, categories[0].ID) +} + +func TestUsers(t *testing.T) { + a := test_setup.SetupTestEnv(t) + db, err := NewConnection() + a.Nil(err) + + q := db.NewQuery(nil, 100) + + err = q.InsertUser(User{ + ID: TEST_USER_ID, + UserLogin: TEST_USER_LOGIN, + DisplayName: TEST_USER_LOGIN, + Email: "", + BroadcasterType: "partner", + UserType: "testing", + UserDescription: "hi mom", + CreatedAt: util.GetTimestamp().Format(time.RFC3339), + ModifiedAt: util.GetTimestamp().Format(time.RFC3339), + CategoryID: sql.NullString{String: "1", Valid: true}, + Title: "hello", + Language: "en", + Delay: 0, + }, false) + a.Nil(err) + + err = q.InsertUser(User{ + ID: "2", + UserLogin: "second_user", + DisplayName: "second_user", + Email: "", + BroadcasterType: "partner", + UserType: "testing", + UserDescription: "hi mom", + CreatedAt: util.GetTimestamp().Format(time.RFC3339), + ModifiedAt: util.GetTimestamp().Format(time.RFC3339), + CategoryID: sql.NullString{String: "", Valid: false}, + Title: "hello", + Language: "en", + Delay: 0, + }, false) + a.Nil(err) + + u, err := q.GetUser(User{ID: TEST_USER_ID}) + a.Nil(err) + a.Equal(TEST_USER_ID, u.ID) + a.Equal(TEST_USER_LOGIN, u.UserLogin) + + dbr, err := q.GetUsers(User{ID: TEST_USER_ID}) + a.Nil(err) + users := dbr.Data.([]User) + a.Len(users, 1) + + dbr, err = q.GetChannels(User{ID: TEST_USER_ID}) + a.Nil(err) + channels := dbr.Data.([]User) + a.Len(channels, 1) + a.Equal(channels[0].CategoryID.String, "1") + + // urp + urp := UserRequestParams{BroadcasterID: TEST_USER_ID, UserID: "2"} + err = q.AddFollow(urp) + a.Nil(err) + + dbr, err = q.GetFollows(urp) + a.Nil(err) + follows := dbr.Data.([]Follow) + a.Len(follows, 1) + + err = q.DeleteFollow(urp.UserID, urp.BroadcasterID) + a.Nil(err) + + err = q.AddBlock(urp) + a.Nil(err) + + dbr, err = q.GetBlocks(urp) + a.Nil(err) + blocks := dbr.Data.([]Block) + a.Len(blocks, 1) + + err = q.DeleteBlock(urp.UserID, urp.BroadcasterID) + a.Nil(err) + + err = q.AddEditor(urp) + a.Nil(err) + + dbr, err = q.GetEditors(User{ID: urp.BroadcasterID}) + a.Nil(err) + editors := dbr.Data.([]Editor) + a.Len(editors, 1) + + err = q.UpdateChannel(urp.BroadcasterID, User{ID: urp.BroadcasterID, UserDescription: "hi mom2"}) + a.Nil(err) + + dbr, err = q.GetUsers(User{ID: TEST_USER_ID}) + a.Nil(err) + users = dbr.Data.([]User) + a.Len(users, 1) + a.Equal("hi mom2", users[0].UserDescription) + + dbr, err = q.SearchChannels("testing_", false) + a.Nil(err) + search := dbr.Data.([]SearchChannel) + a.Len(search, 1) + a.Equal(TEST_USER_ID, search[0].ID) + + dbr, err = q.SearchChannels("testing_", true) + a.Nil(err) + search = dbr.Data.([]SearchChannel) + a.Len(search, 0) +} + +func TestChannelPoints(t *testing.T) { + a := test_setup.SetupTestEnv(t) + db, err := NewConnection() + a.Nil(err) + + q := db.NewQuery(nil, 100) + + bTrue := true + + reward := ChannelPointsReward{ + ID: "1", + BroadcasterID: TEST_USER_ID, + BackgroundColor: "#fff", + IsEnabled: &bTrue, + Cost: 100, + Title: "1234", + RewardPrompt: "", + IsUserInputRequired: false, + IsPaused: false, + IsInStock: false, + ShouldRedemptionsSkipQueue: false, + } + + err = q.InsertChannelPointsReward(reward) + a.Nil(err) + + reward.Cost = 101 + err = q.UpdateChannelPointsReward(reward) + a.Nil(err) + + dbr, err := q.GetChannelPointsReward(reward) + a.Nil(err) + rewards := dbr.Data.([]ChannelPointsReward) + a.Len(rewards, 1) + a.Equal(101, rewards[0].Cost) + + redemption := ChannelPointsRedemption{ + ID: "1", + BroadcasterID: TEST_USER_ID, + UserID: "2", + RedemptionStatus: "CANCELED", + RewardID: reward.ID, + RedeemedAt: util.GetTimestamp().Format(time.RFC3339), + } + + err = q.InsertChannelPointsRedemption(redemption) + a.Nil(err) + + redemption.RedemptionStatus = "TEST" + err = q.UpdateChannelPointsRedemption(redemption) + a.Nil(err) + + dbr, err = q.GetChannelPointsRedemption(redemption, "") + a.Nil(err) + redemptions := dbr.Data.([]ChannelPointsRedemption) + a.Len(redemptions, 1) + + err = q.DeleteChannelPointsReward(redemption.RewardID) + a.Nil(err) +} + +func TestDrops(t *testing.T) { + a := test_setup.SetupTestEnv(t) + db, err := NewConnection() + a.Nil(err) + + q := db.NewQuery(nil, 100) + + e := DropsEntitlement{ + ID: "1", + UserID: TEST_USER_ID, + BenefitID: util.RandomGUID(), + GameID: "1", + Timestamp: util.GetTimestamp().Format(time.RFC3339), + } + + err = q.InsertDropsEntitlement(e) + a.Nil(err) + + dbr, err := q.GetDropsEntitlements(e) + a.Nil(err) + entitlements := dbr.Data.([]DropsEntitlement) + a.Len(entitlements, 1) + a.Equal(e.BenefitID, entitlements[0].BenefitID) +} + +func TestErrors(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + a.False(DatabaseErrorIs(errors.New(""), sqlite3.ErrReadonlyRollback)) +} + +func TestModeration(t *testing.T) { + a := test_setup.SetupTestEnv(t) + db, err := NewConnection() + a.Nil(err) + q := db.NewQuery(nil, 100) + + urp := UserRequestParams{BroadcasterID: TEST_USER_ID, UserID: "2"} + err = q.AddModerator(urp) + a.Nil(err) + + dbr, err := q.GetModerationActionsByBroadcaster(TEST_USER_ID) + a.Nil(err) + moderatorActions := dbr.Data.([]ModeratorAction) + a.Len(moderatorActions, 1) + + dbr, err = q.GetModerators(urp) + a.Nil(err) + moderators := dbr.Data.([]Moderator) + a.Len(moderators, 1) + + dbr, err = q.GetModeratorsForBroadcaster(TEST_USER_ID, "2") + a.Nil(err) + moderators = dbr.Data.([]Moderator) + a.Len(moderators, 1) + + dbr, err = q.GetModeratorEvents(urp) + a.Nil(err) + moderatorActions = dbr.Data.([]ModeratorAction) + a.Len(moderatorActions, 1) + + err = q.RemoveModerator(urp.BroadcasterID, urp.UserID) + a.Nil(err) + + err = q.InsertBan(urp) + a.Nil(err) + + dbr, err = q.GetBans(urp) + a.Nil(err) + bans := dbr.Data.([]Ban) + a.Len(bans, 1) + + dbr, err = q.GetBanEvents(urp) + a.Nil(err) + banEvents := dbr.Data.([]BanEvent) + a.Len(banEvents, 1) + +} + +func TestPolls(t *testing.T) { + a := test_setup.SetupTestEnv(t) + db, err := NewConnection() + a.Nil(err) + q := db.NewQuery(nil, 100) + + poll := Poll{ + ID: "1", + BroadcasterID: TEST_USER_ID, + Title: "test", + BitsVotingEnabled: false, + ChannelPointsVotingEnabled: false, + Status: "ACTIVE", + Duration: 150, + StartedAt: util.GetTimestamp().Format(time.RFC3339), + Choices: []PollsChoice{ + { + ID: "1", + Title: "1234", + Votes: 0, + ChannelPointsVotes: 0, + BitsVotes: 0, + PollID: "1", + }, + { + ID: "2", + Title: "234", + Votes: 0, + ChannelPointsVotes: 0, + BitsVotes: 0, + PollID: "1", + }, + }, + } + + err = q.InsertPoll(poll) + a.Nil(err) + + err = q.UpdatePoll(Poll{ID: "1", BroadcasterID: TEST_USER_ID, Title: "test2"}) + a.Nil(err) + + dbr, err := q.GetPolls(Poll{ID: "1"}) + a.Nil(err) + polls := dbr.Data.([]Poll) + a.Len(polls, 1) + a.Equal("test2", polls[0].Title) +} + +func TestPredictions(t *testing.T) { + a := test_setup.SetupTestEnv(t) + db, err := NewConnection() + a.Nil(err) + q := db.NewQuery(nil, 100) + + prediction := Prediction{ + ID: "1", + BroadcasterID: TEST_USER_ID, + Title: "1234", + WinningOutcomeID: nil, + PredictionWindow: 60, + Status: "ACTIVE", + StartedAt: util.GetTimestamp().Format(time.RFC3339), + Outcomes: []PredictionOutcome{ + { + ID: "1", + Title: "111", + Users: 0, + ChannelPoints: 0, + Color: "BLUE", + PredictionID: "1", + }, + { + ID: "2", + Title: "222", + Users: 0, + ChannelPoints: 0, + Color: "PINK", + PredictionID: "1", + }, + }, + } + + err = q.InsertPrediction(prediction) + a.Nil(err) + + predictionPredition := PredictionPrediction{ + PredictionID: "1", + UserID: TEST_USER_ID, + Amount: 1000, + OutcomeID: prediction.Outcomes[0].ID, + } + + err = q.InsertPredictionPrediction(predictionPredition) + a.Nil(err) + + prediction.WinningOutcomeID = &prediction.Outcomes[0].ID + err = q.UpdatePrediction(prediction) + a.Nil(err) + + dbr, err := q.GetPredictions(Prediction{ID: "1"}) + a.Nil(err) + predictions := dbr.Data.([]Prediction) + a.Len(predictions, 1) + prediction = predictions[0] + a.NotNil(prediction.WinningOutcomeID) + a.NotNil(prediction.Outcomes[0].TopPredictors) +} + +func TestQuery(t *testing.T) { + a := test_setup.SetupTestEnv(t) + db, err := NewConnection() + a.Nil(err) + request, err := http.NewRequest(http.MethodGet, "https://google.com", nil) + a.Nil(err) + + q := request.URL.Query() + q.Set("first", "50") + request.URL.RawQuery = q.Encode() + + query := db.NewQuery(request, 100) + a.Equal(50, query.Limit) + + q.Set("after", query.PaginationCursor) + request.URL.RawQuery = q.Encode() + query = db.NewQuery(request, 100) + + q.Set("before", query.PaginationCursor) + request.URL.RawQuery = q.Encode() + query = db.NewQuery(request, 100) + + q.Set("after", "notbase64") + request.URL.RawQuery = q.Encode() + query = db.NewQuery(request, 100) +} + +func TestStreams(t *testing.T) { + a := test_setup.SetupTestEnv(t) + db, err := NewConnection() + a.Nil(err) + q := db.NewQuery(nil, 100) + s := Stream{ + ID: "1", + UserID: TEST_USER_ID, + StreamType: "live", + ViewerCount: 100, + StartedAt: util.GetTimestamp().Format(time.RFC3339), + IsMature: false, + } + err = q.InsertStream(s, false) + a.Nil(err) + + tag := Tag{ + ID: "1", + Name: "test", + IsAuto: false, + } + + err = q.InsertTag(tag) + a.Nil(err) + + dbr, err := q.GetTags(tag) + a.Nil(err) + tags := dbr.Data.([]Tag) + a.Len(tags, 1) + + err = q.InsertStreamTag(StreamTag{TagID: "1", UserID: "1"}) + a.Nil(err) + + dbr, err = q.GetStreamTags(TEST_USER_ID) + a.Nil(err) + tags = dbr.Data.([]Tag) + a.Len(tags, 1) + + dbr, err = q.GetFollowedStreams(s.UserID) + a.Nil(err) + streams := dbr.Data.([]Stream) + a.Len(streams, 0) + + err = q.AddFollow(UserRequestParams{BroadcasterID: s.UserID, UserID: "2"}) + a.Nil(err) + + dbr, err = q.GetFollowedStreams("2") + a.Nil(err) + streams = dbr.Data.([]Stream) + a.Len(streams, 1) + + dbr, err = q.GetStream(s) + a.Nil(err) + streams = dbr.Data.([]Stream) + a.Len(streams, 1) + stream := streams[0] + a.Len(stream.TagIDs, 1) + + err = q.DeleteAllStreamTags(s.UserID) + a.Nil(err) + + v := Video{ + ID: "1", + StreamID: &s.ID, + BroadcasterID: s.UserID, + Title: "1234", + VideoDescription: "1234", + CreatedAt: util.GetTimestamp().Format(time.RFC3339), + PublishedAt: util.GetTimestamp().Format(time.RFC3339), + Viewable: "public", + ViewCount: 100, + Duration: "1h0m0s", + VideoLanguage: "en", + CategoryID: nil, + Type: "archive", + } + + err = q.InsertVideo(v) + a.Nil(err) + + sm := StreamMarker{ + VideoID: v.ID, + CreatedAt: util.GetTimestamp().Format(time.RFC3339), + PositionSeconds: 10, + Description: "1234", + BroadcasterID: TEST_USER_ID, + ID: "1", + } + + err = q.InsertStreamMarker(sm) + a.Nil(err) + + dbr, err = q.GetStreamMarkers(StreamMarker{BroadcasterID: s.UserID}) + a.Nil(err) + streamTags := dbr.Data.([]StreamMarkerUser) + a.Len(streamTags, 1) +} + +func TestSubscriptions(t *testing.T) { + a := test_setup.SetupTestEnv(t) + db, err := NewConnection() + a.Nil(err) + q := db.NewQuery(nil, 100) + + sub := Subscription{ + BroadcasterID: TEST_USER_ID, + UserID: "2", + IsGift: true, + GifterID: &sql.NullString{String: TEST_USER_ID, Valid: true}, + Tier: "3000", + CreatedAt: util.GetTimestamp().Format(time.RFC3339), + } + + err = q.InsertSubscription(SubscriptionInsert{BroadcasterID: sub.BroadcasterID, UserID: sub.UserID, IsGift: sub.IsGift, GifterID: sub.GifterID, Tier: sub.Tier, CreatedAt: sub.CreatedAt}) + a.Nil(err) + + dbr, err := q.GetSubscriptions(Subscription{BroadcasterID: sub.BroadcasterID, UserID: sub.UserID}) + a.Nil(err) + subs := dbr.Data.([]Subscription) + a.Len(subs, 1) + a.Equal(subs[0].IsGift, true) +} + +func TestTeams(t *testing.T) { + a := test_setup.SetupTestEnv(t) + db, err := NewConnection() + a.Nil(err) + q := db.NewQuery(nil, 100) + + team := Team{ + ID: "1", + CreatedAt: util.GetTimestamp().Format(time.RFC3339), + UpdatedAt: util.GetTimestamp().Format(time.RFC3339), + Info: "", + ThumbnailURL: "", + TeamName: "test", + TeamDisplayName: "Test", + } + + err = q.InsertTeam(team) + a.Nil(err) + + err = q.InsertTeamMember(TeamMember{TeamID: team.ID, UserID: TEST_USER_ID}) + a.Nil(err) + + dbr, err := q.GetTeam(Team{ID: team.ID}) + a.Nil(err) + teams := dbr.Data.([]Team) + a.Len(teams, 1) + + dbr, err = q.GetTeamByBroadcaster(TEST_USER_ID) + a.Nil(err) + teams = dbr.Data.([]Team) + a.Len(teams, 1) + + dbr, err = q.GetTeamByBroadcaster("2") + a.Nil(err) + teams = dbr.Data.([]Team) + a.Len(teams, 0) +} + +func TestVideos(t *testing.T) { + a := test_setup.SetupTestEnv(t) + db, err := NewConnection() + a.Nil(err) + q := db.NewQuery(nil, 100) + + v := Video{ + ID: util.RandomGUID(), + StreamID: nil, + BroadcasterID: TEST_USER_ID, + Title: "1234", + VideoDescription: "1234", + CreatedAt: util.GetTimestamp().Format(time.RFC3339), + PublishedAt: util.GetTimestamp().Format(time.RFC3339), + Viewable: "public", + ViewCount: 100, + Duration: "1h0m0s", + VideoLanguage: "en", + CategoryID: nil, + Type: "archive", + } + + err = q.InsertVideo(v) + a.Nil(err) + + vms := VideoMutedSegment{ + VideoID: v.ID, + VideoOffset: 20, + Duration: 30, + } + + err = q.InsertMutedSegmentsForVideo(vms) + a.Nil(err) + + dbr, err := q.GetVideos(Video{ID: v.ID}, "", "time") + a.Nil(err) + videos := dbr.Data.([]Video) + a.Len(videos, 1) + a.Len(videos[0].MutedSegments, 1) + + c := Clip{ + ID: "1", + BroadcasterID: TEST_USER_ID, + CreatorID: TEST_USER_ID, + VideoID: vms.VideoID, + GameID: "1", + Language: "en", + Title: "?", + ViewCount: 100, + Duration: 1234.5, + CreatedAt: util.GetTimestamp().Format(time.RFC3339), + } + + err = q.InsertClip(c) + a.Nil(err) + + dbr, err = q.GetClips(Clip{ID: c.ID}, "", "") + a.Nil(err) + clips := dbr.Data.([]Clip) + a.Len(clips, 1) + + err = q.DeleteVideo(vms.VideoID) + a.Nil(err) +} diff --git a/internal/database/drops.go b/internal/database/drops.go index 073d8ea2..dffbd5ef 100644 --- a/internal/database/drops.go +++ b/internal/database/drops.go @@ -5,7 +5,7 @@ package database import "log" type DropsEntitlement struct { - ID string `db:"id" json:"id" dbs:"c.id"` + ID string `db:"id" json:"id" dbs:"de.id"` UserID string `db:"user_id" json:"user_id"` BenefitID string `db:"benefit_id" json:"benefit_id"` GameID string `db:"game_id" json:"game_id"` @@ -14,7 +14,7 @@ type DropsEntitlement struct { func (q *Query) GetDropsEntitlements(de DropsEntitlement) (*DBResponse, error) { var r []DropsEntitlement - stmt := generateSQL("select * from drops_entitlements", de, SEP_AND) + stmt := generateSQL("select * from drops_entitlements de", de, SEP_AND) stmt += " order by timestamp desc " + q.SQL rows, err := q.DB.NamedQuery(stmt, de) if err != nil { diff --git a/internal/database/errors.go b/internal/database/errors.go index 038b8f98..3b6ff24f 100644 --- a/internal/database/errors.go +++ b/internal/database/errors.go @@ -2,16 +2,23 @@ // SPDX-License-Identifier: Apache-2.0 package database -import "github.com/mattn/go-sqlite3" +import ( + "reflect" + + "github.com/mattn/go-sqlite3" +) // Wrapper for the various SQLite errors to allow for easier error checking func DatabaseErrorIs(err error, sqliteError error) bool { - tempErr := err.(sqlite3.Error) - switch v := sqliteError.(type) { - case sqlite3.ErrNo: - return tempErr.Code == v - case sqlite3.ErrNoExtended: - return tempErr.ExtendedCode == v + if reflect.TypeOf(err).String() == "sqlite3.Error" { + tempErr := err.(sqlite3.Error) + switch v := sqliteError.(type) { + case sqlite3.ErrNo: + return tempErr.Code == v + case sqlite3.ErrNoExtended: + return tempErr.ExtendedCode == v + } } + return false } diff --git a/internal/database/extensions.go b/internal/database/extensions.go deleted file mode 100644 index 54f9b18e..00000000 --- a/internal/database/extensions.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -package database - -import "log" - -type Extension struct { -} - -func (q *Query) GetExtensionById(id string) (*DBResponse, error) { - var r []Extension - - err := q.DB.Get(&r, "select * from principle where id = $1", id) - if err != nil { - return nil, err - } - log.Printf("%#v", r) - - dbr := DBResponse{ - Data: r, - Limit: q.Limit, - Total: len(r), - } - - if len(r) != q.Limit { - q.PaginationCursor = "" - } - - dbr.Cursor = q.PaginationCursor - - return &dbr, err -} - -func (q *Query) InsertExtension(p Extension, upsert bool) error { - tx := q.DB.MustBegin() - tx.NamedExec(`insert into principle values(:id, :values...)`, p) - return tx.Commit() -} diff --git a/internal/database/init.go b/internal/database/init.go index aa3acda7..b3616222 100644 --- a/internal/database/init.go +++ b/internal/database/init.go @@ -4,6 +4,7 @@ package database import ( "fmt" + "log" "strconv" "github.com/jmoiron/sqlx" @@ -26,7 +27,7 @@ var migrateSQL = map[int]migrateMap{ Message: "Previously executed events are incompatible with new versions of the CLI.", }, 2: { - SQL: `create table categories( id text not null primary key, category_name text not null ); create table users( id text not null primary key, user_login text not null, display_name text not null, email text not null, user_type text, broadcaster_type text, user_description text, created_at text not null, category_id text, modified_at text, stream_language text not null default 'en', title text not null default '', delay int not null default 0, foreign key (category_id) references categories(id) ); create table follows ( broadcaster_id text not null, user_id text not null, created_at text not null, primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table blocks ( broadcaster_id text not null, user_id text not null, created_at text not null, primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table bans ( broadcaster_id text not null, user_id text not null, created_at text not null, primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table ban_events ( id text not null primary key, event_timestamp text not null, event_type text not null, event_version text not null default '1.0', broadcaster_id text not null, user_id text not null, expires_at text, foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table moderators ( broadcaster_id text not null, user_id text not null, created_at text not null, primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table moderator_actions ( id text not null primary key, event_timestamp text not null, event_type text not null, event_version text not null default '1.0', broadcaster_id text not null, user_id text not null, foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table editors ( broadcaster_id text not null, user_id text not null, created_at text not null, primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table channel_points_rewards( id text not null primary key, broadcaster_id text not null, reward_image text, background_color text, is_enabled boolean not null default false, cost int not null default 0, title text not null, reward_prompt text, is_user_input_required boolean default false, stream_max_enabled boolean default false, stream_max_count int default 0, stream_user_max_enabled boolean default false, stream_user_max_count int default 0, global_cooldown_enabled boolean default false, global_cooldown_seconds int default 0, is_paused boolean default false, is_in_stock boolean default true, should_redemptions_skip_queue boolean default false, redemptions_redeemed_current_stream int, cooldown_expires_at text, foreign key (broadcaster_id) references users(id) ); create table channel_points_redemptions( id text not null primary key, reward_id text not null, broadcaster_id text not null, user_id text not null, user_input text, redemption_status text not null, redeemed_at text, foreign key (reward_id) references channel_points_rewards(id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table streams( id text not null primary key, broadcaster_id id text not null, stream_type text not null default 'live', viewer_count int not null, started_at text not null, is_mature boolean not null default false, foreign key (broadcaster_id) references users(id) ); create table tags( id text not null primary key, is_auto boolean not null default false, tag_name text not null ); create table stream_tags( stream_id text not null, tag_id text not null, primary key(stream_id, tag_id), foreign key(stream_id) references streams(id), foreign key(tag_id) references tags(id) ); create table teams( id text not null primary key, background_image_url text, banner text, created_at text not null, updated_at text, info text, thumbnail_url text, team_name text, team_display_name text ); create table team_members( team_id text not null, user_id text not null, primary key (team_id, user_id) foreign key (team_id) references teams(id), foreign key (user_id) references users(id) ); create table videos( id text not null primary key, stream_id text, broadcaster_id text not null, title text not null, video_description text not null, created_at text not null, published_at text, viewable text not null, view_count int not null default 0, duration text not null, video_language text not null default 'en', foreign key (stream_id) references streams(id), foreign key (broadcaster_id) references users(id) ); create table stream_markers( id text not null primary key, video_id text not null, position_seconds int not null, created_at text not null, description text not null, broadcaster_id text not null, foreign key (broadcaster_id) references users(id), foreign key (video_id) references videos(id) ); create table video_muted_segments ( video_id text not null, video_offset int not null, duration int not null, primary key (video_id, video_offset), foreign key (video_id) references videos(id) ); create table subscriptions ( broadcaster_id text not null, user_id text not null, is_gift boolean not null default false, gifter_id text, tier text not null default '1000', created_at text not null, primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id), foreign key (gifter_id) references users(id) ); create table drops_entitlements( id text not null primary key, benefit_id text not null, timestamp text not null, user_id text not null, game_id text not null, foreign key (user_id) references users(id), foreign key (game_id) references categories(id) ); create table clients ( id text not null primary key, secret text not null, is_extension boolean default false, name text not null ); create table authorizations ( id integer not null primary key AUTOINCREMENT, client_id text not null, user_id text, token text not null unique, expires_at text not null, scopes text, foreign key (client_id) references clients(id) ); create table polls ( id text not null primary key, broadcaster_id text not null, title text not null, bits_voting_enabled boolean default false, bits_per_vote int default 10, channel_points_voting_enabled boolean default false, channel_points_per_vote int default 10, status text not null, duration int not null, started_at text not null, ended_at text not null, foreign key (broadcaster_id) references users(id) ); create table poll_choices ( id text not null primary key, title text not null, votes int not null default 0, channel_points_votes int not null default 0, bits_votes int not null default 0, poll_id text not null, foreign key (poll_id) references polls(id) ); create table predictions ( id text not null, broadcaster_id text not null, title text not null, winning_outcome_id text, prediction_window int, status text not null, created_at text not null, ended_at text not null, locked_at text not null, foreign key (broadcaster_id) references users(id) ); create table prediction_outcomes ( id text not null, title text not null, users int not null default 0, channel_points int not null default 0, color text not null ); create table prediction_predictions ( prediction_id text not null, user_id text not null, amount int not null, color text not null, primary key(prediction_id, user_id), foreign key(user_id) references users(id), foreign key(prediction_id) references predictions(id) ); create table clips ( id text not null primary key, broadcaster_id text not null, creator_id text not null, video_id text not null, game_id text not null, title text not null, view_count int default 0, created_at text not null, duration real not null, foreign key (broadcaster_id) references users(id), foreign key (creator_id) references users(id) );`, + SQL: `create table categories( id text not null primary key, category_name text not null ); create table users( id text not null primary key, user_login text not null, display_name text not null, email text not null, user_type text, broadcaster_type text, user_description text, created_at text not null, category_id text, modified_at text, stream_language text not null default 'en', title text not null default '', delay int not null default 0, foreign key (category_id) references categories(id) ); create table follows ( broadcaster_id text not null, user_id text not null, created_at text not null, primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table blocks ( broadcaster_id text not null, user_id text not null, created_at text not null, primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table bans ( broadcaster_id text not null, user_id text not null, created_at text not null, expires_at text, primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table ban_events ( id text not null primary key, event_timestamp text not null, event_type text not null, event_version text not null default '1.0', broadcaster_id text not null, user_id text not null, expires_at text, foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table moderators ( broadcaster_id text not null, user_id text not null, created_at text not null, primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table moderator_actions ( id text not null primary key, event_timestamp text not null, event_type text not null, event_version text not null default '1.0', broadcaster_id text not null, user_id text not null, foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table editors ( broadcaster_id text not null, user_id text not null, created_at text not null, primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table channel_points_rewards( id text not null primary key, broadcaster_id text not null, reward_image text, background_color text, is_enabled boolean not null default false, cost int not null default 0, title text not null, reward_prompt text, is_user_input_required boolean default false, stream_max_enabled boolean default false, stream_max_count int default 0, stream_user_max_enabled boolean default false, stream_user_max_count int default 0, global_cooldown_enabled boolean default false, global_cooldown_seconds int default 0, is_paused boolean default false, is_in_stock boolean default true, should_redemptions_skip_queue boolean default false, redemptions_redeemed_current_stream int, cooldown_expires_at text, foreign key (broadcaster_id) references users(id) ); create table channel_points_redemptions( id text not null primary key, reward_id text not null, broadcaster_id text not null, user_id text not null, user_input text, redemption_status text not null, redeemed_at text, foreign key (reward_id) references channel_points_rewards(id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table streams( id text not null primary key, broadcaster_id id text not null, stream_type text not null default 'live', viewer_count int not null, started_at text not null, is_mature boolean not null default false, foreign key (broadcaster_id) references users(id) ); create table tags( id text not null primary key, is_auto boolean not null default false, tag_name text not null ); create table stream_tags( user_id text not null, tag_id text not null, primary key(user_id, tag_id), foreign key(user_id) references users(id), foreign key(tag_id) references tags(id) ); create table teams( id text not null primary key, background_image_url text, banner text, created_at text not null, updated_at text, info text, thumbnail_url text, team_name text, team_display_name text ); create table team_members( team_id text not null, user_id text not null, primary key (team_id, user_id) foreign key (team_id) references teams(id), foreign key (user_id) references users(id) ); create table videos( id text not null primary key, stream_id text, broadcaster_id text not null, title text not null, video_description text not null, created_at text not null, published_at text, viewable text not null, view_count int not null default 0, duration text not null, video_language text not null default 'en', category_id text, type text default 'archive', foreign key (stream_id) references streams(id), foreign key (broadcaster_id) references users(id), foreign key (category_id) references categories(id) ); create table stream_markers( id text not null primary key, video_id text not null, position_seconds int not null, created_at text not null, description text not null, broadcaster_id text not null, foreign key (broadcaster_id) references users(id), foreign key (video_id) references videos(id) ); create table video_muted_segments ( video_id text not null, video_offset int not null, duration int not null, primary key (video_id, video_offset), foreign key (video_id) references videos(id) ); create table subscriptions ( broadcaster_id text not null, user_id text not null, is_gift boolean not null default false, gifter_id text, tier text not null default '1000', created_at text not null, primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id), foreign key (gifter_id) references users(id) ); create table drops_entitlements( id text not null primary key, benefit_id text not null, timestamp text not null, user_id text not null, game_id text not null, foreign key (user_id) references users(id), foreign key (game_id) references categories(id) ); create table clients ( id text not null primary key, secret text not null, is_extension boolean default false, name text not null ); create table authorizations ( id integer not null primary key AUTOINCREMENT, client_id text not null, user_id text, token text not null unique, expires_at text not null, scopes text, foreign key (client_id) references clients(id) ); create table polls ( id text not null primary key, broadcaster_id text not null, title text not null, bits_voting_enabled boolean default false, bits_per_vote int default 10, channel_points_voting_enabled boolean default false, channel_points_per_vote int default 10, status text not null, duration int not null, started_at text not null, ended_at text, foreign key (broadcaster_id) references users(id) ); create table poll_choices ( id text not null primary key, title text not null, votes int not null default 0, channel_points_votes int not null default 0, bits_votes int not null default 0, poll_id text not null, foreign key (poll_id) references polls(id) ); create table predictions ( id text not null primary key, broadcaster_id text not null, title text not null, winning_outcome_id text, prediction_window int, status text not null, created_at text not null, ended_at text, locked_at text, foreign key (broadcaster_id) references users(id) ); create table prediction_outcomes ( id text not null primary key, title text not null, users int not null default 0, channel_points int not null default 0, color text not null, prediction_id text not null, foreign key (prediction_id) references predictions(id) ); create table prediction_predictions ( prediction_id text not null, user_id text not null, amount int not null, outcome_id text not null, primary key(prediction_id, user_id), foreign key(user_id) references users(id), foreign key(prediction_id) references predictions(id), foreign key(outcome_id) references prediction_outcomes(id) ); create table clips ( id text not null primary key, broadcaster_id text not null, creator_id text not null, video_id text not null, game_id text not null, title text not null, view_count int default 0, created_at text not null, duration real not null, foreign key (broadcaster_id) references users(id), foreign key (creator_id) references users(id) ); `, Message: "Adding mock API tables.", }, } @@ -64,15 +65,26 @@ func checkAndUpdate(db sqlx.DB) error { } func initDatabase(db sqlx.DB) error { - createSQL := `create table events( id text not null primary key, event text not null, json text not null, from_user text not null, to_user text not null, transport text not null, timestamp text not null); create table categories( id text not null primary key, category_name text not null ); create table users( id text not null primary key, user_login text not null, display_name text not null, email text not null, user_type text, broadcaster_type text, user_description text, created_at text not null, category_id text, modified_at text, stream_language text not null default 'en', title text not null default '', delay int not null default 0, foreign key (category_id) references categories(id) ); create table follows ( broadcaster_id text not null, user_id text not null, created_at text not null, primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table blocks ( broadcaster_id text not null, user_id text not null, created_at text not null, primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table bans ( broadcaster_id text not null, user_id text not null, created_at text not null, primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table ban_events ( id text not null primary key, event_timestamp text not null, event_type text not null, event_version text not null default '1.0', broadcaster_id text not null, user_id text not null, expires_at text, foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table moderators ( broadcaster_id text not null, user_id text not null, created_at text not null, primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table moderator_actions ( id text not null primary key, event_timestamp text not null, event_type text not null, event_version text not null default '1.0', broadcaster_id text not null, user_id text not null, foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table editors ( broadcaster_id text not null, user_id text not null, created_at text not null, primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table channel_points_rewards( id text not null primary key, broadcaster_id text not null, reward_image text, background_color text, is_enabled boolean not null default false, cost int not null default 0, title text not null, reward_prompt text, is_user_input_required boolean default false, stream_max_enabled boolean default false, stream_max_count int default 0, stream_user_max_enabled boolean default false, stream_user_max_count int default 0, global_cooldown_enabled boolean default false, global_cooldown_seconds int default 0, is_paused boolean default false, is_in_stock boolean default true, should_redemptions_skip_queue boolean default false, redemptions_redeemed_current_stream int, cooldown_expires_at text, foreign key (broadcaster_id) references users(id) ); create table channel_points_redemptions( id text not null primary key, reward_id text not null, broadcaster_id text not null, user_id text not null, user_input text, redemption_status text not null, redeemed_at text, foreign key (reward_id) references channel_points_rewards(id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table streams( id text not null primary key, broadcaster_id id text not null, stream_type text not null default 'live', viewer_count int not null, started_at text not null, is_mature boolean not null default false, foreign key (broadcaster_id) references users(id) ); create table tags( id text not null primary key, is_auto boolean not null default false, tag_name text not null ); create table stream_tags( stream_id text not null, tag_id text not null, primary key(stream_id, tag_id), foreign key(stream_id) references streams(id), foreign key(tag_id) references tags(id) ); create table teams( id text not null primary key, background_image_url text, banner text, created_at text not null, updated_at text, info text, thumbnail_url text, team_name text, team_display_name text ); create table team_members( team_id text not null, user_id text not null, primary key (team_id, user_id) foreign key (team_id) references teams(id), foreign key (user_id) references users(id) ); create table videos( id text not null primary key, stream_id text, broadcaster_id text not null, title text not null, video_description text not null, created_at text not null, published_at text, viewable text not null, view_count int not null default 0, duration text not null, video_language text not null default 'en', foreign key (stream_id) references streams(id), foreign key (broadcaster_id) references users(id) ); create table stream_markers( id text not null primary key, video_id text not null, position_seconds int not null, created_at text not null, description text not null, broadcaster_id text not null, foreign key (broadcaster_id) references users(id), foreign key (video_id) references videos(id) ); create table video_muted_segments ( video_id text not null, video_offset int not null, duration int not null, primary key (video_id, video_offset), foreign key (video_id) references videos(id) ); create table subscriptions ( broadcaster_id text not null, user_id text not null, is_gift boolean not null default false, gifter_id text, tier text not null default '1000', created_at text not null, primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id), foreign key (gifter_id) references users(id) ); create table drops_entitlements( id text not null primary key, benefit_id text not null, timestamp text not null, user_id text not null, game_id text not null, foreign key (user_id) references users(id), foreign key (game_id) references categories(id) ); create table clients ( id text not null primary key, secret text not null, is_extension boolean default false, name text not null ); create table authorizations ( id integer not null primary key AUTOINCREMENT, client_id text not null, user_id text, token text not null unique, expires_at text not null, scopes text, foreign key (client_id) references clients(id) ); create table polls ( id text not null primary key, broadcaster_id text not null, title text not null, bits_voting_enabled boolean default false, bits_per_vote int default 10, channel_points_voting_enabled boolean default false, channel_points_per_vote int default 10, status text not null, duration int not null, started_at text not null, ended_at text not null, foreign key (broadcaster_id) references users(id) ); create table poll_choices ( id text not null primary key, title text not null, votes int not null default 0, channel_points_votes int not null default 0, bits_votes int not null default 0, poll_id text not null, foreign key (poll_id) references polls(id) ); create table predictions ( id text not null, broadcaster_id text not null, title text not null, winning_outcome_id text, prediction_window int, status text not null, created_at text not null, ended_at text not null, locked_at text not null, foreign key (broadcaster_id) references users(id) ); create table prediction_outcomes ( id text not null, title text not null, users int not null default 0, channel_points int not null default 0, color text not null ); create table prediction_predictions ( prediction_id text not null, user_id text not null, amount int not null, color text not null, primary key(prediction_id, user_id), foreign key(user_id) references users(id), foreign key(prediction_id) references predictions(id) ); create table clips ( id text not null primary key, broadcaster_id text not null, creator_id text not null, video_id text not null, game_id text not null, title text not null, view_count int default 0, created_at text not null, duration real not null, foreign key (broadcaster_id) references users(id), foreign key (creator_id) references users(id) );` - - _, err := db.Exec(createSQL) - if err != nil { - return err + createSQL := `create table events( id text not null primary key, event text not null, json text not null, from_user text not null, to_user text not null, transport text not null, timestamp text not null); create table categories( id text not null primary key, category_name text not null ); create table users( id text not null primary key, user_login text not null, display_name text not null, email text not null, user_type text, broadcaster_type text, user_description text, created_at text not null, category_id text, modified_at text, stream_language text not null default 'en', title text not null default '', delay int not null default 0, foreign key (category_id) references categories(id) ); create table follows ( broadcaster_id text not null, user_id text not null, created_at text not null, primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table blocks ( broadcaster_id text not null, user_id text not null, created_at text not null, primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table bans ( broadcaster_id text not null, user_id text not null, created_at text not null, expires_at text, primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table ban_events ( id text not null primary key, event_timestamp text not null, event_type text not null, event_version text not null default '1.0', broadcaster_id text not null, user_id text not null, expires_at text, foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table moderators ( broadcaster_id text not null, user_id text not null, created_at text not null, primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table moderator_actions ( id text not null primary key, event_timestamp text not null, event_type text not null, event_version text not null default '1.0', broadcaster_id text not null, user_id text not null, foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table editors ( broadcaster_id text not null, user_id text not null, created_at text not null, primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table channel_points_rewards( id text not null primary key, broadcaster_id text not null, reward_image text, background_color text, is_enabled boolean not null default false, cost int not null default 0, title text not null, reward_prompt text, is_user_input_required boolean default false, stream_max_enabled boolean default false, stream_max_count int default 0, stream_user_max_enabled boolean default false, stream_user_max_count int default 0, global_cooldown_enabled boolean default false, global_cooldown_seconds int default 0, is_paused boolean default false, is_in_stock boolean default true, should_redemptions_skip_queue boolean default false, redemptions_redeemed_current_stream int, cooldown_expires_at text, foreign key (broadcaster_id) references users(id) ); create table channel_points_redemptions( id text not null primary key, reward_id text not null, broadcaster_id text not null, user_id text not null, user_input text, redemption_status text not null, redeemed_at text, foreign key (reward_id) references channel_points_rewards(id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) ); create table streams( id text not null primary key, broadcaster_id id text not null, stream_type text not null default 'live', viewer_count int not null, started_at text not null, is_mature boolean not null default false, foreign key (broadcaster_id) references users(id) ); create table tags( id text not null primary key, is_auto boolean not null default false, tag_name text not null ); create table stream_tags( user_id text not null, tag_id text not null, primary key(user_id, tag_id), foreign key(user_id) references users(id), foreign key(tag_id) references tags(id) ); create table teams( id text not null primary key, background_image_url text, banner text, created_at text not null, updated_at text, info text, thumbnail_url text, team_name text, team_display_name text ); create table team_members( team_id text not null, user_id text not null, primary key (team_id, user_id) foreign key (team_id) references teams(id), foreign key (user_id) references users(id) ); create table videos( id text not null primary key, stream_id text, broadcaster_id text not null, title text not null, video_description text not null, created_at text not null, published_at text, viewable text not null, view_count int not null default 0, duration text not null, video_language text not null default 'en', category_id text, type text default 'archive', foreign key (stream_id) references streams(id), foreign key (broadcaster_id) references users(id), foreign key (category_id) references categories(id) ); create table stream_markers( id text not null primary key, video_id text not null, position_seconds int not null, created_at text not null, description text not null, broadcaster_id text not null, foreign key (broadcaster_id) references users(id), foreign key (video_id) references videos(id) ); create table video_muted_segments ( video_id text not null, video_offset int not null, duration int not null, primary key (video_id, video_offset), foreign key (video_id) references videos(id) ); create table subscriptions ( broadcaster_id text not null, user_id text not null, is_gift boolean not null default false, gifter_id text, tier text not null default '1000', created_at text not null, primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id), foreign key (gifter_id) references users(id) ); create table drops_entitlements( id text not null primary key, benefit_id text not null, timestamp text not null, user_id text not null, game_id text not null, foreign key (user_id) references users(id), foreign key (game_id) references categories(id) ); create table clients ( id text not null primary key, secret text not null, is_extension boolean default false, name text not null ); create table authorizations ( id integer not null primary key AUTOINCREMENT, client_id text not null, user_id text, token text not null unique, expires_at text not null, scopes text, foreign key (client_id) references clients(id) ); create table polls ( id text not null primary key, broadcaster_id text not null, title text not null, bits_voting_enabled boolean default false, bits_per_vote int default 10, channel_points_voting_enabled boolean default false, channel_points_per_vote int default 10, status text not null, duration int not null, started_at text not null, ended_at text, foreign key (broadcaster_id) references users(id) ); create table poll_choices ( id text not null primary key, title text not null, votes int not null default 0, channel_points_votes int not null default 0, bits_votes int not null default 0, poll_id text not null, foreign key (poll_id) references polls(id) ); create table predictions ( id text not null primary key, broadcaster_id text not null, title text not null, winning_outcome_id text, prediction_window int, status text not null, created_at text not null, ended_at text, locked_at text, foreign key (broadcaster_id) references users(id) ); create table prediction_outcomes ( id text not null primary key, title text not null, users int not null default 0, channel_points int not null default 0, color text not null, prediction_id text not null, foreign key (prediction_id) references predictions(id) ); create table prediction_predictions ( prediction_id text not null, user_id text not null, amount int not null, outcome_id text not null, primary key(prediction_id, user_id), foreign key(user_id) references users(id), foreign key(prediction_id) references predictions(id), foreign key(outcome_id) references prediction_outcomes(id) ); create table clips ( id text not null primary key, broadcaster_id text not null, creator_id text not null, video_id text not null, game_id text not null, title text not null, view_count int default 0, created_at text not null, duration real not null, foreign key (broadcaster_id) references users(id), foreign key (creator_id) references users(id) ); ` + for i := 1; i <= 5; i++ { + tx := db.MustBegin() + tx.Exec(createSQL) + err := tx.Commit() + if err != nil { + if i == 5 { + return err + } + log.Print(err) + continue + } + if err == nil { + break + } } - _, err = db.Exec("PRAGMA user_version=" + strconv.Itoa(currentVersion)) + _, err := db.Exec("PRAGMA user_version=" + strconv.Itoa(currentVersion)) if err != nil { + log.Print(err) return err } diff --git a/internal/database/moderation.go b/internal/database/moderation.go index 8e32aa37..e6374ec0 100644 --- a/internal/database/moderation.go +++ b/internal/database/moderation.go @@ -72,7 +72,6 @@ func (q *Query) GetModerationActionsByBroadcaster(broadcaster string) (*DBRespon if err != nil { return nil, err } - log.Printf("%#v", r) dbr := DBResponse{ Data: r, diff --git a/internal/database/polls.go b/internal/database/polls.go new file mode 100644 index 00000000..a9755b5d --- /dev/null +++ b/internal/database/polls.go @@ -0,0 +1,84 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package database + +type Poll struct { + ID string `db:"id" dbs:"p.id" json:"id"` + BroadcasterID string `db:"broadcaster_id" dbs:"u1.id" json:"broadcaster_id"` + BroadcasterLogin string `db:"broadcaster_login" dbi:"false" json:"broadcaster_login"` + BroadcasterName string `db:"broadcaster_name" dbi:"false" json:"broadcaster_name"` + Title string `db:"title" dbs:"p.title" json:"title"` + BitsVotingEnabled bool `db:"bits_voting_enabled" json:"bits_voting_enabled"` + BitsPerVote int `db:"bits_per_vote" json:"bits_per_vote"` + ChannelPointsVotingEnabled bool `db:"channel_points_voting_enabled" json:"channel_points_voting_enabled"` + ChannelPointsPerVote int `db:"channel_points_per_vote" json:"channel_points_per_vote"` + Status string `db:"status" json:"status"` + Duration int `db:"duration" json:"duration"` + StartedAt string `db:"started_at" json:"started_at"` + EndedAt string `db:"ended_at" json:"ended_at,omitempty"` + Choices []PollsChoice `json:"choices"` +} + +type PollsChoice struct { + ID string `db:"id" json:"id"` + Title string `db:"title" json:"title"` + Votes int `db:"votes" json:"votes"` + ChannelPointsVotes int `db:"channel_points_votes" json:"channel_points_votes"` + BitsVotes int `db:"bits_votes" json:"bits_votes"` + PollID string `db:"poll_id" json:"-"` +} + +func (q *Query) GetPolls(p Poll) (*DBResponse, error) { + r := []Poll{} + + sql := generateSQL("select p.*, u1.user_login as broadcaster_login, u1.display_name as broadcaster_name from polls p join users u1 on p.broadcaster_id = u1.id", p, SEP_AND) + rows, err := q.DB.NamedQuery(sql, p) + if err != nil { + return nil, err + } + + for rows.Next() { + var p Poll + var pc []PollsChoice + err := rows.StructScan(&p) + if err != nil { + return nil, err + } + + err = q.DB.Select(&pc, "select * from poll_choices where poll_id=$1", p.ID) + if err != nil { + return nil, err + } + + p.Choices = pc + r = append(r, p) + } + + dbr := DBResponse{ + Data: r, + Limit: q.Limit, + Total: len(r), + } + + if len(r) != q.Limit { + q.PaginationCursor = "" + } + + dbr.Cursor = q.PaginationCursor + + return &dbr, err +} + +func (q *Query) InsertPoll(p Poll) error { + tx := q.DB.MustBegin() + tx.NamedExec(generateInsertSQL("polls", "id", p, false), p) + for _, c := range p.Choices { + tx.NamedExec(generateInsertSQL("poll_choices", "id", c, false), c) + } + return tx.Commit() +} + +func (q *Query) UpdatePoll(p Poll) error { + _, err := q.DB.NamedExec(generateUpdateSQL("polls", []string{"id"}, p), p) + return err +} diff --git a/internal/database/predictions.go b/internal/database/predictions.go new file mode 100644 index 00000000..2e2591ba --- /dev/null +++ b/internal/database/predictions.go @@ -0,0 +1,116 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package database + +type Prediction struct { + ID string `db:"id" dbs:"p.id" json:"id"` + BroadcasterID string `db:"broadcaster_id" dbs:"u1.id" json:"broadcaster_id"` + BroadcasterLogin string `db:"broadcaster_login" dbi:"false" json:"broadcaster_login"` + BroadcasterName string `db:"broadcaster_name" dbi:"false" json:"broadcaster_name"` + Title string `db:"title" json:"title"` + WinningOutcomeID *string `db:"winning_outcome_id" json:"winning_outcome_id"` + PredictionWindow int `db:"prediction_window" json:"prediction_window"` + Status string `db:"status" json:"status"` + StartedAt string `db:"created_at" json:"started_at"` + EndedAt *string `db:"ended_at" json:"ended_at"` + LockedAt *string `db:"locked_at" json:"locked_at"` + Outcomes []PredictionOutcome `json:"outcomes"` +} + +type PredictionOutcome struct { + ID string `db:"id" dbs:"po.id" json:"id"` + Title string `db:"title" json:"title"` + Users int `db:"users" json:"users"` + ChannelPoints int `db:"channel_points" json:"channel_points"` + TopPredictors []*PredictionPrediction `json:"top_predictors"` + Color string `db:"color" json:"color"` + PredictionID string `db:"prediction_id" json:"-"` +} +type PredictionPrediction struct { + PredictionID string `db:"prediction_id" json:"-"` + UserID string `db:"user_id" json:"user_id"` + UserLogin string `db:"user_login" dbi:"false" json:"user_login"` + UserName string `db:"user_name" dbi:"false" json:"user_name"` + Amount int `db:"amount" json:"channel_points_used"` + OutcomeID string `db:"outcome_id" json:"-"` + // calculated fields + AmountWon int `json:"channel_points_won"` +} + +func (q *Query) GetPredictions(p Prediction) (*DBResponse, error) { + r := []Prediction{} + + sql := generateSQL("select p.*, u1.user_login as broadcaster_login, u1.display_name as broadcaster_name from predictions p join users u1 on p.broadcaster_id = u1.id", p, SEP_AND) + rows, err := q.DB.NamedQuery(sql, p) + if err != nil { + return nil, err + } + + for rows.Next() { + p := Prediction{} + outcomes := []PredictionOutcome{} + err := rows.StructScan(&p) + if err != nil { + return nil, err + } + err = q.DB.Select(&outcomes, "select po.id, po.title, po.color, COUNT(pp.prediction_id) as users, IFNULL(SUM(pp.amount),0) as channel_points from prediction_outcomes po left join prediction_predictions pp on po.id = pp.outcome_id where po.prediction_id = $1 group by po.id, po.title, po.color", p.ID) + if err != nil { + return nil, err + } + + for i, o := range outcomes { + if p.WinningOutcomeID != nil { + topPredictors := []PredictionPrediction{} + err = q.DB.Select(&topPredictors, "select pp.*, u1.user_login, u1.display_name as user_name from prediction_predictions pp join users u1 on pp.user_id = u1.id where pp.outcome_id = $1 order by pp.amount desc limit 2", o.ID) + if err != nil { + return nil, err + } + tp := []*PredictionPrediction{} + for _, p := range topPredictors { + tp = append(tp, &p) + } + outcomes[i].TopPredictors = tp + + } + if len(outcomes[i].TopPredictors) == 0 { + outcomes[i].TopPredictors = nil + } + } + p.Outcomes = outcomes + r = append(r, p) + } + + dbr := DBResponse{ + Data: r, + Limit: q.Limit, + Total: len(r), + } + + if len(r) != q.Limit { + q.PaginationCursor = "" + } + + dbr.Cursor = q.PaginationCursor + + return &dbr, err +} + +func (q *Query) InsertPrediction(p Prediction) error { + tx := q.DB.MustBegin() + tx.NamedExec(generateInsertSQL("predictions", "id", p, false), p) + + for _, o := range p.Outcomes { + tx.NamedExec(generateInsertSQL("prediction_outcomes", "id", o, false), o) + } + return tx.Commit() +} + +func (q *Query) InsertPredictionPrediction(p PredictionPrediction) error { + _, err := q.DB.NamedExec(generateInsertSQL("prediction_predictions", "user_id", p, false), p) + return err +} + +func (q *Query) UpdatePrediction(p Prediction) error { + _, err := q.DB.NamedExec(generateUpdateSQL("predictions", []string{"id", "broadcaster_id"}, p), p) + return err +} diff --git a/internal/database/sql_gen.go b/internal/database/sql_gen.go index c60efe56..740e0772 100644 --- a/internal/database/sql_gen.go +++ b/internal/database/sql_gen.go @@ -4,10 +4,7 @@ package database import ( "database/sql" - "encoding/base64" - "encoding/json" "fmt" - "log" "reflect" "strings" "time" @@ -205,40 +202,3 @@ func generateUpdateSQL(table string, pk []string, i interface{}) string { } return fmt.Sprintf("%v where %v", s, strings.Join(whereClause, " AND ")) } - -// Generates the respective pagination token -func generatePaginationSQLAndResponse(limit int, prev_cursor string, is_before bool) InternalPagination { - ic := InternalCursor{} - if limit == 0 { - limit = 20 - } - - if prev_cursor != "" { - b, err := base64.RawStdEncoding.DecodeString(prev_cursor) - if err != nil { - log.Print(err) - } - json.Unmarshal(b, &ic) - if is_before { - ic.Offset -= limit - } else { - ic.Offset += limit - } - } - - ic.Limit = limit - - if ic.Offset < 0 { - return InternalPagination{} - } - - b, _ := json.Marshal(ic) - - ip := InternalPagination{ - InternalCursor: ic, - PaginationCursor: base64.RawURLEncoding.EncodeToString(b), - SQL: fmt.Sprintf(" LIMIT %v OFFSET %v", ic.Limit, ic.Offset), - } - - return ip -} diff --git a/internal/database/streams.go b/internal/database/streams.go index 128011c6..eae8cef6 100644 --- a/internal/database/streams.go +++ b/internal/database/streams.go @@ -74,7 +74,7 @@ func (q *Query) GetStream(s Stream) (*DBResponse, error) { var s Stream st := []string{} err := rows.StructScan(&s) - println(s.Language) + if err != nil { return nil, err } @@ -193,7 +193,6 @@ func (q *Query) GetFollowedStreams(userID string) (*DBResponse, error) { for i, s := range r { var st []string - println(s.Language) if err != nil { return nil, err } @@ -203,7 +202,7 @@ func (q *Query) GetFollowedStreams(userID string) (*DBResponse, error) { if s.CategoryName.Valid { r[i].RealCategoryName = s.CategoryName.String } - err = q.DB.Select(&st, "select tag_id from stream_tags where stream_id=$1", s.ID) + err = q.DB.Select(&st, "select tag_id from stream_tags where user_id=$1", s.UserID) if err != nil { return nil, err } @@ -255,9 +254,10 @@ func (q *Query) GetStreamMarkers(sm StreamMarker) (*DBResponse, error) { if err != nil { return nil, err } - for i, _ := range sm { + for i := range sm { sm[i].URL = fmt.Sprintf("https://twitch.tv/%v/manager/highlighter/%v?t=%v", u.BroadcasterLogin, v.ID, calcTOffset(sm[i].PositionSeconds)) } + r[i].Videos = append(r[i].Videos, StreamMarkerVideo{VideoID: v.ID, Markers: sm}) } diff --git a/internal/database/teams.go b/internal/database/teams.go index f0d5b88b..ec6e8406 100644 --- a/internal/database/teams.go +++ b/internal/database/teams.go @@ -39,10 +39,10 @@ func (q *Query) GetTeam(t Team) (*DBResponse, error) { return nil, err } - if *t.BackgroundImageUrl == "" { + if t.BackgroundImageUrl == nil { t.BackgroundImageUrl = nil } - if *t.Banner == "" { + if t.Banner == nil { t.Banner = nil } @@ -92,10 +92,10 @@ func (q *Query) GetTeamByBroadcaster(broadcasterID string) (*DBResponse, error) } for i, t := range r { - if *t.BackgroundImageUrl == "" { + if t.BackgroundImageUrl == nil { r[i].BackgroundImageUrl = nil } - if *t.Banner == "" { + if t.Banner == nil { r[i].Banner = nil } r[i].ThumbnailURL = fmt.Sprintf("https://static-cdn.jtvnw.net/jtv_user_pictures/team-%v-team_logo_image-bf1d9a87ca81432687de60e24ad9593d-600x600.png", t.TeamName) diff --git a/internal/database/videos.go b/internal/database/videos.go index c01407d6..eb65dd2e 100644 --- a/internal/database/videos.go +++ b/internal/database/videos.go @@ -23,7 +23,6 @@ type Video struct { VideoLanguage string `db:"video_language" json:"video_language"` MutedSegments []VideoMutedSegment `json:"muted_segments"` CategoryID *string `db:"category_id" dbs:"v.category_id" json:"-"` - OldCat *string `db:"old_cat" json:"-"` // temp Type string `db:"type" json:"type"` // calculated fields URL string `json:"url"` diff --git a/internal/events/cmd_helpers_test.go b/internal/events/cmd_helpers_test.go index 0d419ee1..c3d435cc 100644 --- a/internal/events/cmd_helpers_test.go +++ b/internal/events/cmd_helpers_test.go @@ -5,18 +5,18 @@ package events import ( "testing" - "github.com/twitchdev/twitch-cli/internal/util" + "github.com/twitchdev/twitch-cli/test_setup" ) func TestValidTriggers(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) t1 := ValidTriggers() a.NotEmpty(t1) } func TestValidTransports(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) t1 := ValidTransports() a.NotEmpty(t1) diff --git a/internal/events/trigger/forward_event_test.go b/internal/events/trigger/forward_event_test.go index 06935714..00a0bbed 100644 --- a/internal/events/trigger/forward_event_test.go +++ b/internal/events/trigger/forward_event_test.go @@ -12,11 +12,11 @@ import ( "testing" "time" - "github.com/twitchdev/twitch-cli/internal/util" + "github.com/twitchdev/twitch-cli/test_setup" ) func TestForwardEventEventsub(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) secret := "potaytoes" @@ -51,7 +51,7 @@ func TestForwardEventEventsub(t *testing.T) { } func TestForwardEventWebsub(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) secret := "potaytoes" diff --git a/internal/events/trigger/retrigger_event_test.go b/internal/events/trigger/retrigger_event_test.go index ed1281d0..a0f1d116 100644 --- a/internal/events/trigger/retrigger_event_test.go +++ b/internal/events/trigger/retrigger_event_test.go @@ -10,11 +10,11 @@ import ( "testing" "github.com/twitchdev/twitch-cli/internal/models" - "github.com/twitchdev/twitch-cli/internal/util" + "github.com/twitchdev/twitch-cli/test_setup" ) func TestRefireEvent(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusAccepted) diff --git a/internal/events/trigger/trigger_event_test.go b/internal/events/trigger/trigger_event_test.go index bbfbd792..d47c3730 100644 --- a/internal/events/trigger/trigger_event_test.go +++ b/internal/events/trigger/trigger_event_test.go @@ -9,11 +9,11 @@ import ( "testing" "github.com/twitchdev/twitch-cli/internal/models" - "github.com/twitchdev/twitch-cli/internal/util" + "github.com/twitchdev/twitch-cli/test_setup" ) func TestFire(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusAccepted) diff --git a/internal/events/types/_template/_event_name_test.go b/internal/events/types/_template/_event_name_test.go index f1a8a1d3..deea40de 100644 --- a/internal/events/types/_template/_event_name_test.go +++ b/internal/events/types/_template/_event_name_test.go @@ -8,14 +8,14 @@ import ( "github.com/twitchdev/twitch-cli/internal/events" "github.com/twitchdev/twitch-cli/internal/models" - "github.com/twitchdev/twitch-cli/internal/util" + "github.com/twitchdev/twitch-cli/test_setup" ) var fromUser = "1234" var toUser = "4567" func TestEventSub(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := *&events.MockEventParameters{ FromUserID: fromUser, @@ -35,7 +35,7 @@ func TestEventSub(t *testing.T) { } func TestWebSub(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := *&events.MockEventParameters{ FromUserID: fromUser, @@ -54,7 +54,7 @@ func TestWebSub(t *testing.T) { // write tests here for websub } func TestFakeTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := *&events.MockEventParameters{ FromUserID: fromUser, @@ -68,7 +68,7 @@ func TestFakeTransport(t *testing.T) { a.Empty(r) } func TestValidTrigger(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTrigger("trigger_keyword") a.Equal(true, r) @@ -78,7 +78,7 @@ func TestValidTrigger(t *testing.T) { } func TestValidTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTransport(models.TransportEventSub) a.Equal(true, r) @@ -87,7 +87,7 @@ func TestValidTransport(t *testing.T) { a.Equal(false, r) } func TestGetTopic(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.GetTopic(models.TransportEventSub, "trigger_keyword") a.NotNil(r) diff --git a/internal/events/types/authorization_revoke/authorization_revoke_test.go b/internal/events/types/authorization_revoke/authorization_revoke_test.go index 7f3eb282..a26a7f11 100644 --- a/internal/events/types/authorization_revoke/authorization_revoke_test.go +++ b/internal/events/types/authorization_revoke/authorization_revoke_test.go @@ -9,14 +9,14 @@ import ( "github.com/spf13/viper" "github.com/twitchdev/twitch-cli/internal/events" "github.com/twitchdev/twitch-cli/internal/models" - "github.com/twitchdev/twitch-cli/internal/util" + "github.com/twitchdev/twitch-cli/test_setup" ) var fromUser = "1234" var toUser = "4567" func TestEventSub(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) viper.Set("ClientID", "1234") params := *&events.MockEventParameters{ @@ -38,7 +38,7 @@ func TestEventSub(t *testing.T) { a.Equal("1234", body.Event.ClientID) } func TestWebSub(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := *&events.MockEventParameters{ FromUserID: fromUser, @@ -53,7 +53,7 @@ func TestWebSub(t *testing.T) { // write tests here for websub } func TestFakeTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := *&events.MockEventParameters{ FromUserID: fromUser, @@ -67,7 +67,7 @@ func TestFakeTransport(t *testing.T) { a.Empty(r) } func TestValidTrigger(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTrigger("revoke") a.Equal(true, r) @@ -77,7 +77,7 @@ func TestValidTrigger(t *testing.T) { } func TestValidTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTransport(models.TransportEventSub) a.Equal(true, r) @@ -86,7 +86,7 @@ func TestValidTransport(t *testing.T) { a.Equal(false, r) } func TestGetTopic(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.GetTopic(models.TransportEventSub, "revoke") a.NotNil(r) diff --git a/internal/events/types/ban/ban_test.go b/internal/events/types/ban/ban_test.go index e370b350..03def55c 100644 --- a/internal/events/types/ban/ban_test.go +++ b/internal/events/types/ban/ban_test.go @@ -8,14 +8,14 @@ import ( "github.com/twitchdev/twitch-cli/internal/events" "github.com/twitchdev/twitch-cli/internal/models" - "github.com/twitchdev/twitch-cli/internal/util" + "github.com/twitchdev/twitch-cli/test_setup" ) var fromUser = "1234" var toUser = "4567" func TestEventSubBan(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := events.MockEventParameters{ FromUserID: fromUser, ToUserID: toUser, @@ -53,7 +53,7 @@ func TestEventSubBan(t *testing.T) { } func TestWebSubBan(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := *&events.MockEventParameters{ FromUserID: fromUser, @@ -91,7 +91,7 @@ func TestWebSubBan(t *testing.T) { } func TestFakeTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := events.MockEventParameters{ FromUserID: fromUser, @@ -106,7 +106,7 @@ func TestFakeTransport(t *testing.T) { } func TestValidTrigger(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTrigger("ban") a.Equal(true, r) @@ -119,7 +119,7 @@ func TestValidTrigger(t *testing.T) { } func TestValidTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTransport(models.TransportEventSub) a.Equal(true, r) @@ -129,7 +129,7 @@ func TestValidTransport(t *testing.T) { } func TestGetTopic(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.GetTopic(models.TransportEventSub, "ban") a.NotNil(r) diff --git a/internal/events/types/channel_points_redemption/redemption_event_test.go b/internal/events/types/channel_points_redemption/redemption_event_test.go index 0fcaed83..eead8fc5 100644 --- a/internal/events/types/channel_points_redemption/redemption_event_test.go +++ b/internal/events/types/channel_points_redemption/redemption_event_test.go @@ -8,14 +8,14 @@ import ( "github.com/twitchdev/twitch-cli/internal/events" "github.com/twitchdev/twitch-cli/internal/models" - "github.com/twitchdev/twitch-cli/internal/util" + "github.com/twitchdev/twitch-cli/test_setup" ) var fromUser = "1234" var toUser = "4567" func TestEventSub(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := events.MockEventParameters{ Transport: models.TransportEventSub, @@ -57,7 +57,7 @@ func TestEventSub(t *testing.T) { a.NotNil(body.Event.Reward.ID) } func TestWebsubRedemption(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := events.MockEventParameters{ Transport: models.TransportWebSub, @@ -68,7 +68,7 @@ func TestWebsubRedemption(t *testing.T) { } func TestFakeTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := events.MockEventParameters{ FromUserID: fromUser, @@ -82,7 +82,7 @@ func TestFakeTransport(t *testing.T) { a.Empty(r) } func TestValidTrigger(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTrigger("add-redemption") a.Equal(true, r) @@ -92,7 +92,7 @@ func TestValidTrigger(t *testing.T) { } func TestValidTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTransport(models.TransportEventSub) a.Equal(true, r) @@ -101,7 +101,7 @@ func TestValidTransport(t *testing.T) { a.Equal(false, r) } func TestGetTopic(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.GetTopic(models.TransportEventSub, "add-redemption") a.NotNil(r) diff --git a/internal/events/types/channel_points_reward/reward_event_test.go b/internal/events/types/channel_points_reward/reward_event_test.go index 60d7102d..3e45bfd3 100644 --- a/internal/events/types/channel_points_reward/reward_event_test.go +++ b/internal/events/types/channel_points_reward/reward_event_test.go @@ -8,14 +8,14 @@ import ( "github.com/twitchdev/twitch-cli/internal/events" "github.com/twitchdev/twitch-cli/internal/models" - "github.com/twitchdev/twitch-cli/internal/util" + "github.com/twitchdev/twitch-cli/test_setup" ) var fromUser = "1234" var toUser = "4567" func TestEventSub(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := events.MockEventParameters{ Transport: models.TransportEventSub, @@ -39,7 +39,7 @@ func TestEventSub(t *testing.T) { } func TestWebSub(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := events.MockEventParameters{ Transport: models.TransportWebSub, @@ -51,7 +51,7 @@ func TestWebSub(t *testing.T) { } func TestFakeTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := events.MockEventParameters{ FromUserID: fromUser, @@ -66,7 +66,7 @@ func TestFakeTransport(t *testing.T) { } func TestValidTrigger(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTrigger("add-reward") a.Equal(true, r) @@ -76,7 +76,7 @@ func TestValidTrigger(t *testing.T) { } func TestValidTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTransport(models.TransportEventSub) a.Equal(true, r) @@ -85,7 +85,7 @@ func TestValidTransport(t *testing.T) { a.Equal(false, r) } func TestGetTopic(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.GetTopic(models.TransportEventSub, "add-reward") a.NotNil(r) diff --git a/internal/events/types/cheer/cheer_event_test.go b/internal/events/types/cheer/cheer_event_test.go index b985ad38..a6205507 100644 --- a/internal/events/types/cheer/cheer_event_test.go +++ b/internal/events/types/cheer/cheer_event_test.go @@ -8,14 +8,14 @@ import ( "github.com/twitchdev/twitch-cli/internal/events" "github.com/twitchdev/twitch-cli/internal/models" - "github.com/twitchdev/twitch-cli/internal/util" + "github.com/twitchdev/twitch-cli/test_setup" ) var fromUser = "1234" var toUser = "4567" func TestEventsubCheer(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := events.MockEventParameters{ FromUserID: fromUser, ToUserID: toUser, @@ -54,7 +54,7 @@ func TestEventsubCheer(t *testing.T) { } func TestWebsubCheer(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := events.MockEventParameters{ Transport: models.TransportWebSub, @@ -65,7 +65,7 @@ func TestWebsubCheer(t *testing.T) { } func TestFakeTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := events.MockEventParameters{ FromUserID: fromUser, @@ -80,7 +80,7 @@ func TestFakeTransport(t *testing.T) { } func TestValidTrigger(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTrigger("cheer") a.Equal(true, r) @@ -90,7 +90,7 @@ func TestValidTrigger(t *testing.T) { } func TestValidTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTransport(models.TransportEventSub) a.Equal(true, r) @@ -99,7 +99,7 @@ func TestValidTransport(t *testing.T) { a.Equal(false, r) } func TestGetTopic(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.GetTopic(models.TransportEventSub, "cheer") a.NotNil(r) diff --git a/internal/events/types/extension_transaction/transaction_event_test.go b/internal/events/types/extension_transaction/transaction_event_test.go index 6816c59c..9b146f4c 100644 --- a/internal/events/types/extension_transaction/transaction_event_test.go +++ b/internal/events/types/extension_transaction/transaction_event_test.go @@ -8,14 +8,14 @@ import ( "github.com/twitchdev/twitch-cli/internal/events" "github.com/twitchdev/twitch-cli/internal/models" - "github.com/twitchdev/twitch-cli/internal/util" + "github.com/twitchdev/twitch-cli/test_setup" ) var fromUser = "1234" var toUser = "4567" func TestWebusbTransaction(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := events.MockEventParameters{ FromUserID: fromUser, @@ -37,7 +37,7 @@ func TestWebusbTransaction(t *testing.T) { } func TestEventsubTransaction(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := events.MockEventParameters{ FromUserID: fromUser, @@ -51,7 +51,7 @@ func TestEventsubTransaction(t *testing.T) { } func TestFakeTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := events.MockEventParameters{ FromUserID: fromUser, @@ -66,7 +66,7 @@ func TestFakeTransport(t *testing.T) { } func TestValidTrigger(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTrigger("transaction") a.Equal(true, r) @@ -76,7 +76,7 @@ func TestValidTrigger(t *testing.T) { } func TestValidTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTransport(models.TransportWebSub) a.Equal(true, r) @@ -85,7 +85,7 @@ func TestValidTransport(t *testing.T) { a.Equal(false, r) } func TestGetTopic(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.GetTopic(models.TransportWebSub, "transaction") a.NotNil(r) diff --git a/internal/events/types/follow/follow_event_test.go b/internal/events/types/follow/follow_event_test.go index 1bba46de..7d09a82b 100644 --- a/internal/events/types/follow/follow_event_test.go +++ b/internal/events/types/follow/follow_event_test.go @@ -8,14 +8,14 @@ import ( "github.com/twitchdev/twitch-cli/internal/events" "github.com/twitchdev/twitch-cli/internal/models" - "github.com/twitchdev/twitch-cli/internal/util" + "github.com/twitchdev/twitch-cli/test_setup" ) var fromUser = "1234" var toUser = "4567" func TestEventSub(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := *&events.MockEventParameters{ FromUserID: fromUser, @@ -36,7 +36,7 @@ func TestEventSub(t *testing.T) { } func TestWebSub(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := *&events.MockEventParameters{ FromUserID: fromUser, @@ -56,7 +56,7 @@ func TestWebSub(t *testing.T) { a.Equal(fromUser, body.Data[0].FromID, "Expected from user %v, got %v", fromUser, body.Data[0].FromID) } func TestFakeTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := *&events.MockEventParameters{ FromUserID: fromUser, @@ -70,7 +70,7 @@ func TestFakeTransport(t *testing.T) { a.Empty(r) } func TestValidTrigger(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTrigger("notreal") a.Equal(false, r) @@ -80,7 +80,7 @@ func TestValidTrigger(t *testing.T) { } func TestValidTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTransport(models.TransportEventSub) a.Equal(true, r) @@ -89,7 +89,7 @@ func TestValidTransport(t *testing.T) { a.Equal(false, r) } func TestGetTopic(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.GetTopic(models.TransportEventSub, "follow") a.NotNil(r) diff --git a/internal/events/types/hype_train/hype_train_event_test.go b/internal/events/types/hype_train/hype_train_event_test.go index 80a4a268..eb766c98 100644 --- a/internal/events/types/hype_train/hype_train_event_test.go +++ b/internal/events/types/hype_train/hype_train_event_test.go @@ -8,18 +8,18 @@ import ( "github.com/twitchdev/twitch-cli/internal/events" "github.com/twitchdev/twitch-cli/internal/models" - "github.com/twitchdev/twitch-cli/internal/util" + "github.com/twitchdev/twitch-cli/test_setup" ) var toUser = "4567" func TestEventSub(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := *&events.MockEventParameters{ - ToUserID: toUser, - Transport: models.TransportEventSub, - Trigger: "hype-train-begin", + ToUserID: toUser, + Transport: models.TransportEventSub, + Trigger: "hype-train-begin", } r, err := Event{}.GenerateEvent(params) @@ -33,9 +33,9 @@ func TestEventSub(t *testing.T) { a.Equal(toUser, body.Event.BroadcasterUserID, "Expected to user %v, got %v", toUser, body.Event.BroadcasterUserID) params = *&events.MockEventParameters{ - ToUserID: toUser, - Transport: models.TransportEventSub, - Trigger: "hype-train-progress", + ToUserID: toUser, + Transport: models.TransportEventSub, + Trigger: "hype-train-progress", } r, err = Event{}.GenerateEvent(params) @@ -49,9 +49,9 @@ func TestEventSub(t *testing.T) { a.Equal(toUser, body.Event.BroadcasterUserID, "Expected to user %v, got %v", toUser, body.Event.BroadcasterUserID) params = *&events.MockEventParameters{ - ToUserID: toUser, - Transport: models.TransportEventSub, - Trigger: "hype-train-end", + ToUserID: toUser, + Transport: models.TransportEventSub, + Trigger: "hype-train-end", } r, err = Event{}.GenerateEvent(params) @@ -63,16 +63,16 @@ func TestEventSub(t *testing.T) { a.Equal("channel.hype_train.end", body.Subscription.Type, "Expected event type %v, got %v", "channel.hype_train.end", body.Subscription.Type) a.Equal(toUser, body.Event.BroadcasterUserID, "Expected to user %v, got %v", toUser, body.Event.BroadcasterUserID) - + } func TestWebSub(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := *&events.MockEventParameters{ - ToUserID: toUser, - Transport: models.TransportWebSub, - Trigger: "hype-train-progress", + ToUserID: toUser, + Transport: models.TransportWebSub, + Trigger: "hype-train-progress", } r, err := Event{}.GenerateEvent(params) @@ -84,20 +84,19 @@ func TestWebSub(t *testing.T) { a.Equal("hypetrain.progression", body.Data[0].EventType, "Expected event type %v, got %v", "hypetrain.progression", body.Data[0].EventType) a.Equal(toUser, body.Data[0].EventData.BroadcasterID, "Expected to user %v, got %v", toUser, body.Data[0].EventData.BroadcasterID) - params = *&events.MockEventParameters{ - ToUserID: toUser, - Transport: models.TransportWebSub, + ToUserID: toUser, + Transport: models.TransportWebSub, } } func TestFakeTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := *&events.MockEventParameters{ - ToUserID: toUser, - Transport: "fake_transport", - Trigger: "hype-train-progress", + ToUserID: toUser, + Transport: "fake_transport", + Trigger: "hype-train-progress", } r, err := Event{}.GenerateEvent(params) @@ -105,7 +104,7 @@ func TestFakeTransport(t *testing.T) { a.Empty(r) } func TestValidTrigger(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTrigger("hype-train-begin") a.Equal(true, r) @@ -119,7 +118,7 @@ func TestValidTrigger(t *testing.T) { } func TestValidTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTransport(models.TransportWebSub) a.Equal(true, r) @@ -129,8 +128,8 @@ func TestValidTransport(t *testing.T) { } func TestGetTopic(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.GetTopic(models.TransportWebSub, "hype-train-progress") a.Equal("hypetrain.progression", r, "Expected %v, got %v", "hypetrain.progression", r) -} \ No newline at end of file +} diff --git a/internal/events/types/moderator_change/moderator_change_event_test.go b/internal/events/types/moderator_change/moderator_change_event_test.go index 2a5b937c..f33ff263 100644 --- a/internal/events/types/moderator_change/moderator_change_event_test.go +++ b/internal/events/types/moderator_change/moderator_change_event_test.go @@ -8,14 +8,14 @@ import ( "github.com/twitchdev/twitch-cli/internal/events" "github.com/twitchdev/twitch-cli/internal/models" - "github.com/twitchdev/twitch-cli/internal/util" + "github.com/twitchdev/twitch-cli/test_setup" ) var fromUser = "1234" var toUser = "4567" func TestEventSub(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := *&events.MockEventParameters{ FromUserID: fromUser, @@ -37,7 +37,7 @@ func TestEventSub(t *testing.T) { } func TestWebSub(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := *&events.MockEventParameters{ FromUserID: fromUser, @@ -64,7 +64,7 @@ func TestWebSub(t *testing.T) { } } func TestFakeTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := *&events.MockEventParameters{ FromUserID: fromUser, @@ -78,7 +78,7 @@ func TestFakeTransport(t *testing.T) { a.Empty(r) } func TestValidTrigger(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTrigger("add-moderator") a.Equal(true, r) @@ -91,7 +91,7 @@ func TestValidTrigger(t *testing.T) { } func TestValidTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTransport(models.TransportWebSub) a.Equal(true, r) @@ -101,7 +101,7 @@ func TestValidTransport(t *testing.T) { } func TestGetTopic(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.GetTopic(models.TransportWebSub, "add-moderator") a.Equal("moderation.moderator.add", r, "Expected %v, got %v", "moderation.moderator.add", r) diff --git a/internal/events/types/raid/raid_test.go b/internal/events/types/raid/raid_test.go index e5b92365..ca67f0c7 100644 --- a/internal/events/types/raid/raid_test.go +++ b/internal/events/types/raid/raid_test.go @@ -8,14 +8,14 @@ import ( "github.com/twitchdev/twitch-cli/internal/events" "github.com/twitchdev/twitch-cli/internal/models" - "github.com/twitchdev/twitch-cli/internal/util" + "github.com/twitchdev/twitch-cli/test_setup" ) var fromUser = "1234" var toUser = "4567" func TestEventSub(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := *&events.MockEventParameters{ FromUserID: fromUser, @@ -35,7 +35,7 @@ func TestEventSub(t *testing.T) { } func TestWebSub(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := *&events.MockEventParameters{ FromUserID: fromUser, @@ -50,7 +50,7 @@ func TestWebSub(t *testing.T) { // write tests here for websub } func TestFakeTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := *&events.MockEventParameters{ FromUserID: fromUser, @@ -64,7 +64,7 @@ func TestFakeTransport(t *testing.T) { a.Empty(r) } func TestValidTrigger(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTrigger("raid") a.Equal(true, r) @@ -74,7 +74,7 @@ func TestValidTrigger(t *testing.T) { } func TestValidTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTransport(models.TransportEventSub) a.Equal(true, r) @@ -83,7 +83,7 @@ func TestValidTransport(t *testing.T) { a.Equal(false, r) } func TestGetTopic(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.GetTopic(models.TransportEventSub, "trigger_keyword") a.NotNil(r) diff --git a/internal/events/types/stream_change/stream_change_event_test.go b/internal/events/types/stream_change/stream_change_event_test.go index b3256212..1293bbd1 100644 --- a/internal/events/types/stream_change/stream_change_event_test.go +++ b/internal/events/types/stream_change/stream_change_event_test.go @@ -8,14 +8,14 @@ import ( "github.com/twitchdev/twitch-cli/internal/events" "github.com/twitchdev/twitch-cli/internal/models" - "github.com/twitchdev/twitch-cli/internal/util" + "github.com/twitchdev/twitch-cli/test_setup" ) var fromUser = "1234" var toUser = "4567" func TestEventSub(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := *&events.MockEventParameters{ FromUserID: fromUser, @@ -53,7 +53,7 @@ func TestEventSub(t *testing.T) { } func TestWebSubStreamChange(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) newStreamTitle := "Awesome new title from the CLI!" @@ -77,7 +77,7 @@ func TestWebSubStreamChange(t *testing.T) { a.Equal(newStreamTitle, body.Data[0].StreamTitle, "Expected new stream title, got %v", body.Data[0].StreamTitle) } func TestFakeTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := *&events.MockEventParameters{ FromUserID: fromUser, @@ -91,7 +91,7 @@ func TestFakeTransport(t *testing.T) { a.Empty(r) } func TestValidTrigger(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTrigger("stream-change") a.Equal(true, r) @@ -101,7 +101,7 @@ func TestValidTrigger(t *testing.T) { } func TestValidTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTransport(models.TransportEventSub) a.Equal(true, r) @@ -110,7 +110,7 @@ func TestValidTransport(t *testing.T) { a.Equal(false, r) } func TestGetTopic(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.GetTopic(models.TransportEventSub, "stream-change") a.NotNil(r) diff --git a/internal/events/types/streamdown/streamdown_test.go b/internal/events/types/streamdown/streamdown_test.go index f4adb9a0..b05a39bd 100644 --- a/internal/events/types/streamdown/streamdown_test.go +++ b/internal/events/types/streamdown/streamdown_test.go @@ -8,14 +8,14 @@ import ( "github.com/twitchdev/twitch-cli/internal/events" "github.com/twitchdev/twitch-cli/internal/models" - "github.com/twitchdev/twitch-cli/internal/util" + "github.com/twitchdev/twitch-cli/test_setup" ) var fromUser = "1234" var toUser = "4567" func TestEventSub(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := *&events.MockEventParameters{ FromUserID: fromUser, @@ -35,7 +35,7 @@ func TestEventSub(t *testing.T) { } func TestWebSub(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := *&events.MockEventParameters{ FromUserID: fromUser, @@ -55,7 +55,7 @@ func TestWebSub(t *testing.T) { } func TestFakeTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := *&events.MockEventParameters{ FromUserID: fromUser, @@ -69,7 +69,7 @@ func TestFakeTransport(t *testing.T) { a.Empty(r) } func TestValidTrigger(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTrigger("streamdown") a.Equal(true, r) @@ -79,7 +79,7 @@ func TestValidTrigger(t *testing.T) { } func TestValidTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTransport(models.TransportEventSub) a.Equal(true, r) @@ -88,7 +88,7 @@ func TestValidTransport(t *testing.T) { a.Equal(false, r) } func TestGetTopic(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.GetTopic(models.TransportEventSub, "streamdown") a.NotNil(r) diff --git a/internal/events/types/streamup/streamup_test.go b/internal/events/types/streamup/streamup_test.go index 7e18ce63..3ff1e2eb 100644 --- a/internal/events/types/streamup/streamup_test.go +++ b/internal/events/types/streamup/streamup_test.go @@ -8,14 +8,14 @@ import ( "github.com/twitchdev/twitch-cli/internal/events" "github.com/twitchdev/twitch-cli/internal/models" - "github.com/twitchdev/twitch-cli/internal/util" + "github.com/twitchdev/twitch-cli/test_setup" ) var fromUser = "1234" var toUser = "4567" func TestEventSub(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := *&events.MockEventParameters{ FromUserID: fromUser, @@ -35,7 +35,7 @@ func TestEventSub(t *testing.T) { } func TestWebSub(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := *&events.MockEventParameters{ FromUserID: fromUser, @@ -55,7 +55,7 @@ func TestWebSub(t *testing.T) { } func TestFakeTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := *&events.MockEventParameters{ FromUserID: fromUser, @@ -69,7 +69,7 @@ func TestFakeTransport(t *testing.T) { a.Empty(r) } func TestValidTrigger(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTrigger("streamup") a.Equal(true, r) @@ -79,7 +79,7 @@ func TestValidTrigger(t *testing.T) { } func TestValidTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTransport(models.TransportEventSub) a.Equal(true, r) @@ -88,7 +88,7 @@ func TestValidTransport(t *testing.T) { a.Equal(false, r) } func TestGetTopic(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.GetTopic(models.TransportEventSub, "streamup") a.NotNil(r) diff --git a/internal/events/types/subscribe/sub_event_test.go b/internal/events/types/subscribe/sub_event_test.go index 889cbed5..ae09f143 100644 --- a/internal/events/types/subscribe/sub_event_test.go +++ b/internal/events/types/subscribe/sub_event_test.go @@ -8,14 +8,14 @@ import ( "github.com/twitchdev/twitch-cli/internal/events" "github.com/twitchdev/twitch-cli/internal/models" - "github.com/twitchdev/twitch-cli/internal/util" + "github.com/twitchdev/twitch-cli/test_setup" ) var fromUser = "1234" var toUser = "4567" func TestEventSub(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := *&events.MockEventParameters{ FromUserID: fromUser, @@ -36,7 +36,7 @@ func TestEventSub(t *testing.T) { } func TestWebSub(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := *&events.MockEventParameters{ FromUserID: fromUser, @@ -79,7 +79,7 @@ func TestWebSub(t *testing.T) { a.Equal(true, body.Data[0].EventData.IsGift) } func TestFakeTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) params := *&events.MockEventParameters{ FromUserID: fromUser, @@ -93,7 +93,7 @@ func TestFakeTransport(t *testing.T) { a.Empty(r) } func TestValidTrigger(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTrigger("gift") a.Equal(true, r) @@ -103,7 +103,7 @@ func TestValidTrigger(t *testing.T) { } func TestValidTransport(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.ValidTransport(models.TransportEventSub) a.Equal(true, r) @@ -113,7 +113,7 @@ func TestValidTransport(t *testing.T) { } func TestGetTopic(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := Event{}.GetTopic(models.TransportEventSub, "subscribe") a.NotNil(r) diff --git a/internal/events/verify/subscription_verify_test.go b/internal/events/verify/subscription_verify_test.go index e4c730c5..23cb9921 100644 --- a/internal/events/verify/subscription_verify_test.go +++ b/internal/events/verify/subscription_verify_test.go @@ -10,11 +10,11 @@ import ( "testing" "github.com/twitchdev/twitch-cli/internal/models" - "github.com/twitchdev/twitch-cli/internal/util" + "github.com/twitchdev/twitch-cli/test_setup" ) func TestSubscriptionVerify(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var challenge string diff --git a/internal/login/login_request_test.go b/internal/login/login_request_test.go index 369546aa..e36279fe 100644 --- a/internal/login/login_request_test.go +++ b/internal/login/login_request_test.go @@ -8,11 +8,11 @@ import ( "net/http/httptest" "testing" - "github.com/twitchdev/twitch-cli/internal/util" + "github.com/twitchdev/twitch-cli/test_setup" ) func TestLoginRequest(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) var ok = "{\"status\":\"ok\"}" ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/internal/login/login_test.go b/internal/login/login_test.go index 9a2dbd47..3ac4e7d8 100644 --- a/internal/login/login_test.go +++ b/internal/login/login_test.go @@ -12,6 +12,7 @@ import ( "github.com/spf13/viper" "github.com/twitchdev/twitch-cli/internal/util" + "github.com/twitchdev/twitch-cli/test_setup" ) var params = LoginParameters{ @@ -34,7 +35,7 @@ var response = LoginResponse{ } func TestClientCredentialsLogin(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { qp := r.URL.Query() @@ -55,7 +56,7 @@ func TestClientCredentialsLogin(t *testing.T) { } func TestCredentialsLogout(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { qp := r.URL.Query() @@ -84,7 +85,7 @@ func TestCredentialsLogout(t *testing.T) { } func TestGenerateState(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) state, err := generateState() @@ -93,7 +94,7 @@ func TestGenerateState(t *testing.T) { } func TestStoreInConfig(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r := response.Response storeInConfig(r.AccessToken, r.RefreshToken, r.Scope, response.ExpiresAt) @@ -105,7 +106,7 @@ func TestStoreInConfig(t *testing.T) { } func TestRefreshUserToken(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { qp := r.URL.Query() @@ -134,7 +135,7 @@ func TestRefreshUserToken(t *testing.T) { } func TestUserAuthServer(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) state, err := generateState() a.Nil(err) diff --git a/internal/mock_api/endpoints/bits/leaderboard.go b/internal/mock_api/endpoints/bits/leaderboard.go index 03349df1..67753c2e 100644 --- a/internal/mock_api/endpoints/bits/leaderboard.go +++ b/internal/mock_api/endpoints/bits/leaderboard.go @@ -96,8 +96,8 @@ func getBitsLeaderboard(w http.ResponseWriter, r *http.Request) { // check if provided period is not valid (not one of the valid values) if isValidPeriod(period) == false { - w.Write(mock_errors.GetErrorBytes(http.StatusBadRequest, errors.New("Invalid Request"), "Period is invalid")) w.WriteHeader(http.StatusBadRequest) + w.Write(mock_errors.GetErrorBytes(http.StatusBadRequest, errors.New("Invalid Request"), "Period is invalid")) return } @@ -109,14 +109,14 @@ func getBitsLeaderboard(w http.ResponseWriter, r *http.Request) { // validate count param c, err := strconv.Atoi(count) if err != nil { - w.Write(mock_errors.GetErrorBytes(http.StatusBadRequest, errors.New("Invalid Request"), "count is not a valid integer")) w.WriteHeader(http.StatusBadRequest) + w.Write(mock_errors.GetErrorBytes(http.StatusBadRequest, errors.New("Invalid Request"), "count is not a valid integer")) return } if c < 0 || c > 100 { - w.Write(mock_errors.GetErrorBytes(http.StatusBadRequest, errors.New("Invalid Request"), "count must be between 1 and 100")) w.WriteHeader(http.StatusBadRequest) + w.Write(mock_errors.GetErrorBytes(http.StatusBadRequest, errors.New("Invalid Request"), "count must be between 1 and 100")) return } @@ -174,8 +174,8 @@ func getBitsLeaderboard(w http.ResponseWriter, r *http.Request) { u, err := db.NewQuery(r, 100).GetUsers(p) if err != nil { - w.Write(mock_errors.GetErrorBytes(http.StatusInternalServerError, err, "Error getting users for mock.")) w.WriteHeader(http.StatusInternalServerError) + w.Write(mock_errors.GetErrorBytes(http.StatusInternalServerError, err, "Error getting users for mock.")) return } users := u.Data.([]database.User) diff --git a/internal/mock_api/endpoints/clips/clips.go b/internal/mock_api/endpoints/clips/clips.go index 5161c74f..e5c51071 100644 --- a/internal/mock_api/endpoints/clips/clips.go +++ b/internal/mock_api/endpoints/clips/clips.go @@ -83,7 +83,6 @@ func getClips(w http.ResponseWriter, r *http.Request) { if startedAt != "" && endedAt == "" { sa, _ := time.Parse(time.RFC3339, startedAt) endedAt = sa.Add(7 * 24 * time.Hour).Format(time.RFC3339) - println(startedAt, endedAt) } dbr, err := db.NewQuery(r, 100).GetClips(database.Clip{ID: id, BroadcasterID: broadcasterID, GameID: gameID}, startedAt, endedAt) diff --git a/internal/mock_api/endpoints/endpoints.go b/internal/mock_api/endpoints/endpoints.go index 4e84bce5..548ccd9b 100644 --- a/internal/mock_api/endpoints/endpoints.go +++ b/internal/mock_api/endpoints/endpoints.go @@ -13,6 +13,8 @@ import ( "github.com/twitchdev/twitch-cli/internal/mock_api/endpoints/drops" "github.com/twitchdev/twitch-cli/internal/mock_api/endpoints/hype_train" "github.com/twitchdev/twitch-cli/internal/mock_api/endpoints/moderation" + "github.com/twitchdev/twitch-cli/internal/mock_api/endpoints/polls" + "github.com/twitchdev/twitch-cli/internal/mock_api/endpoints/predictions" "github.com/twitchdev/twitch-cli/internal/mock_api/endpoints/search" "github.com/twitchdev/twitch-cli/internal/mock_api/endpoints/streams" "github.com/twitchdev/twitch-cli/internal/mock_api/endpoints/subscriptions" @@ -43,6 +45,8 @@ func All() []mock_api.MockEndpoint { moderation.Bans{}, moderation.ModeratorEvents{}, moderation.Moderators{}, + polls.Polls{}, + predictions.Predictions{}, search.SearchCategories{}, search.SearchChannels{}, streams.AllTags{}, diff --git a/internal/mock_api/endpoints/endpoints_test.go b/internal/mock_api/endpoints/endpoints_test.go new file mode 100644 index 00000000..b872def5 --- /dev/null +++ b/internal/mock_api/endpoints/endpoints_test.go @@ -0,0 +1,139 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package endpoints + +import ( + "fmt" + "net/http" + "testing" + + "github.com/twitchdev/twitch-cli/internal/mock_api/endpoints/bits" + "github.com/twitchdev/twitch-cli/internal/mock_api/endpoints/categories" + "github.com/twitchdev/twitch-cli/test_setup" + "github.com/twitchdev/twitch-cli/test_setup/test_server" +) + +// used to share the same db connection and to run them in parallel +func TestCheermotes(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + ts := test_server.SetupTestServer(bits.Cheermotes{}) + + req, _ := http.NewRequest(http.MethodGet, ts.URL+bits.Cheermotes{}.Path(), nil) + + resp, err := http.DefaultClient.Do(req) + a.Nil(err) + a.NotNil(resp) +} + +func TestBitsLeaderboard(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + ts := test_server.SetupTestServer(bits.BitsLeaderboard{}) + + req, _ := http.NewRequest(http.MethodGet, ts.URL+bits.BitsLeaderboard{}.Path(), nil) + q := req.URL.Query() + q.Set("period", "day") + req.URL.RawQuery = q.Encode() + resp, err := http.DefaultClient.Do(req) + a.Nil(err, fmt.Sprint(err)) + a.Equal(http.StatusOK, resp.StatusCode) + + req, _ = http.NewRequest(http.MethodGet, ts.URL+bits.BitsLeaderboard{}.Path(), nil) + q.Set("period", "week") + req.URL.RawQuery = q.Encode() + resp, err = http.DefaultClient.Do(req) + a.Nil(err, fmt.Sprint(err)) + a.Equal(http.StatusOK, resp.StatusCode) + + req, _ = http.NewRequest(http.MethodGet, ts.URL+bits.BitsLeaderboard{}.Path(), nil) + q.Set("period", "month") + req.URL.RawQuery = q.Encode() + resp, err = http.DefaultClient.Do(req) + a.Nil(err, fmt.Sprint(err)) + a.Equal(http.StatusOK, resp.StatusCode) + + req, _ = http.NewRequest(http.MethodGet, ts.URL+bits.BitsLeaderboard{}.Path(), nil) + q.Set("period", "week") + req.URL.RawQuery = q.Encode() + resp, err = http.DefaultClient.Do(req) + a.Nil(err, fmt.Sprint(err)) + a.Equal(http.StatusOK, resp.StatusCode) + + req, _ = http.NewRequest(http.MethodGet, ts.URL+bits.BitsLeaderboard{}.Path(), nil) + q.Set("period", "potato") + req.URL.RawQuery = q.Encode() + resp, err = http.DefaultClient.Do(req) + a.Nil(err, fmt.Sprint(err)) + a.Equal(http.StatusBadRequest, resp.StatusCode) + + req, _ = http.NewRequest(http.MethodGet, ts.URL+bits.BitsLeaderboard{}.Path(), nil) + q.Set("count", "1") + q.Set("period", "") + req.URL.RawQuery = q.Encode() + resp, err = http.DefaultClient.Do(req) + a.Nil(err, fmt.Sprint(err)) + a.Equal(http.StatusOK, resp.StatusCode) + + req, _ = http.NewRequest(http.MethodGet, ts.URL+bits.BitsLeaderboard{}.Path(), nil) + q.Set("count", "potato") + req.URL.RawQuery = q.Encode() + resp, err = http.DefaultClient.Do(req) + a.Nil(err, fmt.Sprint(err)) + a.Equal(http.StatusBadRequest, resp.StatusCode) + + req, _ = http.NewRequest(http.MethodGet, ts.URL+bits.BitsLeaderboard{}.Path(), nil) + q.Set("count", "1000") + req.URL.RawQuery = q.Encode() + resp, err = http.DefaultClient.Do(req) + a.Nil(err, fmt.Sprint(err)) + a.Equal(http.StatusBadRequest, resp.StatusCode) + + req, _ = http.NewRequest(http.MethodGet, ts.URL+bits.BitsLeaderboard{}.Path(), nil) + q.Set("started_at", "2021") + req.URL.RawQuery = q.Encode() + resp, err = http.DefaultClient.Do(req) + a.Nil(err, fmt.Sprint(err)) + a.Equal(http.StatusBadRequest, resp.StatusCode) +} + +func TestGames(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + ts := test_server.SetupTestServer(categories.Games{}) + + req, _ := http.NewRequest(http.MethodGet, ts.URL+categories.Games{}.Path(), nil) + q := req.URL.Query() + resp, err := http.DefaultClient.Do(req) + a.Nil(err) + a.NotNil(resp) + + req, _ = http.NewRequest(http.MethodGet, ts.URL+categories.Games{}.Path(), nil) + q.Set("id", "1") + req.URL.RawQuery = q.Encode() + resp, err = http.DefaultClient.Do(req) + a.Nil(err) + a.Equal(http.StatusOK, resp.StatusCode) + + req, _ = http.NewRequest(http.MethodGet, ts.URL+categories.Games{}.Path(), nil) + q.Set("name", "day") + req.URL.RawQuery = q.Encode() + resp, err = http.DefaultClient.Do(req) + a.Nil(err) + a.Equal(http.StatusOK, resp.StatusCode) +} + +func TestTopGames(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + ts := test_server.SetupTestServer(categories.TopGames{}) + + req, _ := http.NewRequest(http.MethodGet, ts.URL+categories.TopGames{}.Path(), nil) + q := req.URL.Query() + q.Set("name", "day") + req.URL.RawQuery = q.Encode() + resp, err := http.DefaultClient.Do(req) + a.Nil(err) + a.Equal(http.StatusOK, resp.StatusCode) + +} diff --git a/internal/mock_api/endpoints/polls/_endpoint_name.go b/internal/mock_api/endpoints/polls/_endpoint_name.go deleted file mode 100644 index ec171b4d..00000000 --- a/internal/mock_api/endpoints/polls/_endpoint_name.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -package endpoint_name - -import ( - "net/http" - - "github.com/twitchdev/twitch-cli/internal/database" -) - -var endpointMethodsSupported = map[string]bool{ - http.MethodGet: true, - http.MethodPost: false, - http.MethodDelete: false, - http.MethodPatch: false, - http.MethodPut: false, -} - -var endpointScopesByMethod = map[string][]string{ - http.MethodGet: {}, - http.MethodPost: {}, - http.MethodDelete: {}, - http.MethodPatch: {}, - http.MethodPut: {}, -} - -type Endpoint struct{} - -func (e Endpoint) Path() string { return "/endpoint" } - -func (e Endpoint) GetRequiredScopes(method string) []string { - return endpointScopesByMethod[method] -} - -func (e Endpoint) ValidMethod(method string) bool { - return endpointMethodsSupported[method] -} - -func (e Endpoint) ServeHTTP(w http.ResponseWriter, r *http.Request) { - db = r.Context().Value("db").(database.CLIDatabase) - - w.WriteHeader(200) -} diff --git a/internal/mock_api/endpoints/polls/polls.go b/internal/mock_api/endpoints/polls/polls.go new file mode 100644 index 00000000..79b7a93d --- /dev/null +++ b/internal/mock_api/endpoints/polls/polls.go @@ -0,0 +1,247 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package polls + +import ( + "encoding/json" + "net/http" + "time" + + "github.com/twitchdev/twitch-cli/internal/database" + "github.com/twitchdev/twitch-cli/internal/mock_api/authentication" + "github.com/twitchdev/twitch-cli/internal/mock_api/mock_errors" + "github.com/twitchdev/twitch-cli/internal/models" + "github.com/twitchdev/twitch-cli/internal/util" +) + +var pollsMethodsSupported = map[string]bool{ + http.MethodGet: true, + http.MethodPost: true, + http.MethodDelete: false, + http.MethodPatch: true, + http.MethodPut: false, +} + +var pollsScopesByMethod = map[string][]string{ + http.MethodGet: {"channel:read:polls", "channel:manage:polls"}, + http.MethodPost: {"channel:manage:polls"}, + http.MethodDelete: {}, + http.MethodPatch: {"channel:manage:polls"}, + http.MethodPut: {}, +} + +type Polls struct{} + +type PostPollsBody struct { + BroadcasterID string `json:"broadcaster_id"` + Title string `json:"title"` + Choices []PostPollsBodyChoice `json:"choices"` + Duration int `json:"duration"` + BitsVotingEnabled bool `json:"bits_voting_enabled"` + BitsPerVote int `json:"bits_per_vote"` + ChannelPointsVotingEnabled bool `json:"channel_points_voting_enabled"` + ChannelPointsPerVote int `json:"channel_points_per_vote"` +} + +type PostPollsBodyChoice struct { + Title string `json:"title"` +} + +type PatchPollsBody struct { + BroadcasterID string `json:"broadcaster_id"` + ID string `json:"id"` + Status string `json:"status"` +} + +func (e Polls) Path() string { return "/polls" } + +func (e Polls) GetRequiredScopes(method string) []string { + return pollsScopesByMethod[method] +} + +func (e Polls) ValidMethod(method string) bool { + return pollsMethodsSupported[method] +} + +func (e Polls) ServeHTTP(w http.ResponseWriter, r *http.Request) { + db = r.Context().Value("db").(database.CLIDatabase) + + switch r.Method { + case http.MethodGet: + getPolls(w, r) + break + case http.MethodPost: + postPolls(w, r) + break + case http.MethodPatch: + patchPolls(w, r) + break + default: + w.WriteHeader(http.StatusMethodNotAllowed) + } +} +func getPolls(w http.ResponseWriter, r *http.Request) { + userCtx := r.Context().Value("auth").(authentication.UserAuthentication) + var dbr *database.DBResponse + var err error + polls := []database.Poll{} + + if !userCtx.MatchesBroadcasterIDParam(r) { + mock_errors.WriteBadRequest(w, "broadcaster_id does not match token") + return + } + + ids := r.URL.Query()["id"] + + for _, id := range ids { + dbr, err := db.NewQuery(r, 100).GetPolls(database.Poll{ID: id}) + if err != nil { + mock_errors.WriteServerError(w, "error fetching polls") + return + } + polls = append(polls, dbr.Data.([]database.Poll)...) + } + if len(ids) == 0 { + dbr, err = db.NewQuery(r, 100).GetPolls(database.Poll{BroadcasterID: userCtx.UserID}) + if err != nil { + mock_errors.WriteServerError(w, "error fetching polls") + return + } + polls = append(polls, dbr.Data.([]database.Poll)...) + } + + apiResposne := models.APIResponse{ + Data: polls, + } + + if dbr.Cursor != "" { + apiResposne.Pagination = &models.APIPagination{ + Cursor: dbr.Cursor, + } + } + + bytes, _ := json.Marshal(apiResposne) + w.Write(bytes) +} + +func postPolls(w http.ResponseWriter, r *http.Request) { + userCtx := r.Context().Value("auth").(authentication.UserAuthentication) + + u, err := db.NewQuery(r, 100).GetUser(database.User{ID: userCtx.UserID}) + if err != nil { + mock_errors.WriteBadRequest(w, "error getting user") + return + } + + var body PostPollsBody + + err = json.NewDecoder(r.Body).Decode(&body) + if err != nil { + mock_errors.WriteBadRequest(w, "error reading body") + return + } + + if body.BroadcasterID != userCtx.UserID { + mock_errors.WriteBadRequest(w, "broadcaster_id does not match token") + return + } + + if body.Title == "" { + mock_errors.WriteBadRequest(w, "title is required") + return + } + + if len(body.Choices) < 2 || len(body.Choices) > 5 { + mock_errors.WriteBadRequest(w, "you may only have between 2 and 5 choices") + return + } + + if body.Duration < 15 || body.Duration > 1800 { + mock_errors.WriteBadRequest(w, "duation must be at least15 and at most 1800") + return + } + poll := database.Poll{ + ID: util.RandomGUID(), + BroadcasterID: userCtx.UserID, + BroadcasterLogin: u.UserLogin, + BroadcasterName: u.DisplayName, + Title: body.Title, + BitsVotingEnabled: body.BitsVotingEnabled, + BitsPerVote: body.BitsPerVote, + ChannelPointsVotingEnabled: body.ChannelPointsVotingEnabled, + ChannelPointsPerVote: body.ChannelPointsPerVote, + Status: "ACTIVE", + Duration: body.Duration, + StartedAt: util.GetTimestamp().Format(time.RFC3339), + } + + for _, c := range body.Choices { + if c.Title == "" { + mock_errors.WriteBadRequest(w, "each choice must have a title") + return + } + + poll.Choices = append(poll.Choices, database.PollsChoice{ + ID: util.RandomGUID(), + Title: c.Title, + Votes: 0, + ChannelPointsVotes: 0, + BitsVotes: 0, + PollID: poll.ID, + }) + } + + err = db.NewQuery(r, 100).InsertPoll(poll) + if err != nil { + mock_errors.WriteServerError(w, "error inserting poll") + return + } + + json.NewEncoder(w).Encode(models.APIResponse{Data: []database.Poll{poll}}) +} + +func patchPolls(w http.ResponseWriter, r *http.Request) { + userCtx := r.Context().Value("auth").(authentication.UserAuthentication) + + var body PatchPollsBody + + err := json.NewDecoder(r.Body).Decode(&body) + if err != nil { + mock_errors.WriteBadRequest(w, "error reading body") + return + } + if body.BroadcasterID != userCtx.UserID { + mock_errors.WriteBadRequest(w, "broadcaster_id does not match token") + return + } + if body.ID == "" { + mock_errors.WriteBadRequest(w, "id is required") + return + } + if body.Status != "TERMINATED" && body.Status != "ARCHIVED" { + mock_errors.WriteBadRequest(w, "status must be one of TERMINATED or ARCHIVED") + return + } + + err = db.NewQuery(r, 100).UpdatePoll(database.Poll{ID: body.ID, Status: body.Status, EndedAt: util.GetTimestamp().Format(time.RFC3339)}) + + dbr, err := db.NewQuery(r, 100).GetPolls(database.Poll{BroadcasterID: userCtx.UserID, ID: body.ID}) + if err != nil { + println(err.Error()) + mock_errors.WriteServerError(w, "error fetching polls") + return + } + + apiResposne := models.APIResponse{ + Data: dbr.Data, + } + + if dbr.Cursor != "" { + apiResposne.Pagination = &models.APIPagination{ + Cursor: dbr.Cursor, + } + } + + bytes, _ := json.Marshal(apiResposne) + w.Write(bytes) +} diff --git a/internal/mock_api/endpoints/polls/shared.go b/internal/mock_api/endpoints/polls/shared.go index 9492e07f..3bc6ab25 100644 --- a/internal/mock_api/endpoints/polls/shared.go +++ b/internal/mock_api/endpoints/polls/shared.go @@ -1,6 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package endpoint_name +package polls import "github.com/twitchdev/twitch-cli/internal/database" diff --git a/internal/mock_api/endpoints/predictions/_endpoint_name.go b/internal/mock_api/endpoints/predictions/_endpoint_name.go deleted file mode 100644 index ec171b4d..00000000 --- a/internal/mock_api/endpoints/predictions/_endpoint_name.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -package endpoint_name - -import ( - "net/http" - - "github.com/twitchdev/twitch-cli/internal/database" -) - -var endpointMethodsSupported = map[string]bool{ - http.MethodGet: true, - http.MethodPost: false, - http.MethodDelete: false, - http.MethodPatch: false, - http.MethodPut: false, -} - -var endpointScopesByMethod = map[string][]string{ - http.MethodGet: {}, - http.MethodPost: {}, - http.MethodDelete: {}, - http.MethodPatch: {}, - http.MethodPut: {}, -} - -type Endpoint struct{} - -func (e Endpoint) Path() string { return "/endpoint" } - -func (e Endpoint) GetRequiredScopes(method string) []string { - return endpointScopesByMethod[method] -} - -func (e Endpoint) ValidMethod(method string) bool { - return endpointMethodsSupported[method] -} - -func (e Endpoint) ServeHTTP(w http.ResponseWriter, r *http.Request) { - db = r.Context().Value("db").(database.CLIDatabase) - - w.WriteHeader(200) -} diff --git a/internal/mock_api/endpoints/predictions/predictions.go b/internal/mock_api/endpoints/predictions/predictions.go new file mode 100644 index 00000000..2f68bffb --- /dev/null +++ b/internal/mock_api/endpoints/predictions/predictions.go @@ -0,0 +1,242 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package predictions + +import ( + "encoding/json" + "log" + "net/http" + "time" + + "github.com/twitchdev/twitch-cli/internal/database" + "github.com/twitchdev/twitch-cli/internal/mock_api/authentication" + "github.com/twitchdev/twitch-cli/internal/mock_api/mock_errors" + "github.com/twitchdev/twitch-cli/internal/models" + "github.com/twitchdev/twitch-cli/internal/util" +) + +var predictionsMethodsSupported = map[string]bool{ + http.MethodGet: true, + http.MethodPost: true, + http.MethodDelete: false, + http.MethodPatch: true, + http.MethodPut: false, +} + +var predictionsScopesByMethod = map[string][]string{ + http.MethodGet: {"channel:read:predictions", "channel:manage:predictions"}, + http.MethodPost: {"channel:manage:predictions"}, + http.MethodDelete: {}, + http.MethodPatch: {"channel:manage:predictions"}, + http.MethodPut: {}, +} + +type Predictions struct{} + +type PostPredictionsBody struct { + BroadcasterID string `json:"broadcaster_id"` + Title string `json:"title"` + Outcomes []PostPredictionsBodyOutcomes `json:"outcomes"` + PredictionWindow int `json:"prediction_window"` +} + +type PostPredictionsBodyOutcomes struct { + Title string `json:"title"` +} + +type PatchPredictionsBody struct { + BroadcasterID string `json:"broadcaster_id"` + ID string `json:"id"` + Status string `json:"status"` + WinningOutcomeID string `json:"winning_outcome_id"` +} + +func (e Predictions) Path() string { return "/predictions" } + +func (e Predictions) GetRequiredScopes(method string) []string { + return predictionsScopesByMethod[method] +} + +func (e Predictions) ValidMethod(method string) bool { + return predictionsMethodsSupported[method] +} + +func (e Predictions) ServeHTTP(w http.ResponseWriter, r *http.Request) { + db = r.Context().Value("db").(database.CLIDatabase) + + switch r.Method { + case http.MethodGet: + getPredictions(w, r) + break + case http.MethodPost: + postPredictions(w, r) + break + case http.MethodPatch: + patchPredictions(w, r) + break + default: + w.WriteHeader(http.StatusMethodNotAllowed) + } +} + +func getPredictions(w http.ResponseWriter, r *http.Request) { + userCtx := r.Context().Value("auth").(authentication.UserAuthentication) + predictions := []database.Prediction{} + var dbr *database.DBResponse + var err error + + if !userCtx.MatchesBroadcasterIDParam(r) { + mock_errors.WriteBadRequest(w, "broadcaster_id does not match token") + return + } + + ids := r.URL.Query()["id"] + + for _, id := range ids { + dbr, err := db.NewQuery(r, 100).GetPredictions(database.Prediction{ID: id, BroadcasterID: userCtx.UserID}) + if err != nil { + mock_errors.WriteServerError(w, "error fetching predictions") + return + } + predictions = append(predictions, dbr.Data.([]database.Prediction)...) + } + if len(ids) == 0 { + dbr, err = db.NewQuery(r, 100).GetPredictions(database.Prediction{BroadcasterID: userCtx.UserID}) + if err != nil { + log.Print(err) + mock_errors.WriteServerError(w, "error fetching predictions") + return + } + predictions = append(predictions, dbr.Data.([]database.Prediction)...) + } + + apiResposne := models.APIResponse{ + Data: predictions, + } + + if dbr.Cursor != "" { + apiResposne.Pagination = &models.APIPagination{ + Cursor: dbr.Cursor, + } + } + + bytes, _ := json.Marshal(apiResposne) + w.Write(bytes) +} + +func postPredictions(w http.ResponseWriter, r *http.Request) { + userCtx := r.Context().Value("auth").(authentication.UserAuthentication) + var body PostPredictionsBody + + u, err := db.NewQuery(r, 100).GetUser(database.User{ID: userCtx.UserID}) + if err != nil { + mock_errors.WriteBadRequest(w, "error getting user") + return + } + + err = json.NewDecoder(r.Body).Decode(&body) + if err != nil { + mock_errors.WriteBadRequest(w, "error reading body") + return + } + + if userCtx.UserID != body.BroadcasterID { + mock_errors.WriteBadRequest(w, "broadcaster_id does not match token") + return + } + + if body.Title == "" || len(body.Title) > 45 { + mock_errors.WriteBadRequest(w, "title is required and must be less than 45 characters") + return + } + + if len(body.Outcomes) != 2 { + mock_errors.WriteBadRequest(w, "outcomes must be exactly two items") + return + } + + prediction := database.Prediction{ + ID: util.RandomGUID(), + BroadcasterID: userCtx.UserID, + BroadcasterLogin: u.UserLogin, + BroadcasterName: u.DisplayName, + Title: body.Title, + WinningOutcomeID: nil, + Status: "ACTIVE", + StartedAt: util.GetTimestamp().Format(time.RFC3339), + PredictionWindow: body.PredictionWindow, + } + + for i, o := range body.Outcomes { + color := "BLUE" + if o.Title == "" { + mock_errors.WriteBadRequest(w, "title is required for each outcome") + return + } + if i == 1 { + color = "PINK" + } + prediction.Outcomes = append(prediction.Outcomes, database.PredictionOutcome{ + ID: util.RandomGUID(), + Title: o.Title, + Users: 0, + ChannelPoints: 0, + Color: color, + PredictionID: prediction.ID, + }) + } + err = db.NewQuery(r, 100).InsertPrediction(prediction) + if err != nil { + mock_errors.WriteBadRequest(w, "error inserting prediction") + return + } + + json.NewEncoder(w).Encode(models.APIResponse{Data: []database.Prediction{prediction}}) +} + +func patchPredictions(w http.ResponseWriter, r *http.Request) { + userCtx := r.Context().Value("auth").(authentication.UserAuthentication) + var body PatchPredictionsBody + + err := json.NewDecoder(r.Body).Decode(&body) + if err != nil { + mock_errors.WriteBadRequest(w, "error reading body") + return + } + + if userCtx.UserID != body.BroadcasterID { + mock_errors.WriteBadRequest(w, "broadcaster_id does not match token") + return + } + + if body.ID == "" { + mock_errors.WriteBadRequest(w, "id is required") + return + } + + if body.Status != "RESOLVED" && body.Status != "CANCELED" && body.Status != "LOCKED" { + mock_errors.WriteBadRequest(w, "status must be one of RESOLVED or CANCELED or LOCEKD") + return + } + + if body.Status == "RESOLVED" && body.WinningOutcomeID == "" { + mock_errors.WriteBadRequest(w, "winning_outcome_id is required if status is RESOLVED") + return + } + + err = db.NewQuery(r, 100).UpdatePrediction(database.Prediction{ID: body.ID, Status: body.Status, WinningOutcomeID: &body.WinningOutcomeID}) + if err != nil { + mock_errors.WriteBadRequest(w, "error updating prediction") + return + } + + dbr, err := db.NewQuery(r, 100).GetPredictions(database.Prediction{ID: body.ID, BroadcasterID: body.BroadcasterID}) + if err != nil { + mock_errors.WriteBadRequest(w, "error fetching prediction") + return + } + + prediction := dbr.Data.([]database.Prediction) + + json.NewEncoder(w).Encode(models.APIResponse{Data: prediction}) +} diff --git a/internal/mock_api/endpoints/predictions/shared.go b/internal/mock_api/endpoints/predictions/shared.go index 9492e07f..c4f11ca1 100644 --- a/internal/mock_api/endpoints/predictions/shared.go +++ b/internal/mock_api/endpoints/predictions/shared.go @@ -1,6 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package endpoint_name +package predictions import "github.com/twitchdev/twitch-cli/internal/database" diff --git a/internal/mock_api/generate/generate_test.go b/internal/mock_api/generate/generate_test.go new file mode 100644 index 00000000..8fe30c33 --- /dev/null +++ b/internal/mock_api/generate/generate_test.go @@ -0,0 +1,26 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package generate + +import ( + "testing" + + "github.com/twitchdev/twitch-cli/test_setup" +) + +func TestGenerateUsername(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + un := generateUsername() + a.NotEmpty(un) +} + +func TestGenerate(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + err := Generate(0) + a.Nil(err) + + err = Generate(30) + a.Nil(err) +} diff --git a/internal/request/request_test.go b/internal/request/request_test.go index c759d57b..b100a324 100644 --- a/internal/request/request_test.go +++ b/internal/request/request_test.go @@ -5,11 +5,11 @@ package request import ( "testing" - "github.com/twitchdev/twitch-cli/internal/util" + "github.com/twitchdev/twitch-cli/test_setup" ) func TestNewRequest(t *testing.T) { - a := util.SetupTestEnv(t) + a := test_setup.SetupTestEnv(t) r, err := NewRequest("GET", "https://api.twitch.tv/helix/users", nil) a.Nil(err) diff --git a/test_setup/test_server/test_server.go b/test_setup/test_server/test_server.go new file mode 100644 index 00000000..206e3f9b --- /dev/null +++ b/test_setup/test_server/test_server.go @@ -0,0 +1,62 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package test_server + +import ( + "context" + "log" + "net/http" + "net/http/httptest" + + "github.com/twitchdev/twitch-cli/internal/database" + "github.com/twitchdev/twitch-cli/internal/mock_api" + "github.com/twitchdev/twitch-cli/internal/mock_api/authentication" +) + +func SetupTestServer(next mock_api.MockEndpoint) *httptest.Server { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx := context.Background() + + // just stub it all + db, err := database.NewConnection() + if err != nil { + log.Fatalf("Error connecting to database: %v", err.Error()) + return + } + ctx = context.WithValue(ctx, "db", db) + ctx = context.WithValue(ctx, "auth", authentication.UserAuthentication{Scopes: []string{ + "analytics:read:extensions", + "analytics:read:games", + "bits:read", + "channel:edit:commercial", + "channel:manage:broadcast", + "channel:manage:extensions", + "channel:manage:polls", + "channel:manage:predictions", + "channel:manage:redemptions", + "channel:manage:videos", + "channel:read:editors", + "channel:read:hype_train", + "channel:read:polls", + "channel:read:predictions", + "channel:read:redemptions", + "channel:read:stream_key", + "channel:read:subscriptions", + "clips:edit", + "moderation:read", + "moderator:manage:automod", + "user:edit", + "user:edit:follows", + "user:read:email", + "user:manage:blocked_users", + "user:read:blocked_users", + "user:read:broadcast", + "user:read:follows", + "user:read:subscriptions", + }, UserID: "1", ClientID: "1"}) + r = r.WithContext(ctx) + + next.ServeHTTP(w, r) + })) + return ts +} diff --git a/internal/util/test_setup.go b/test_setup/test_setup.go similarity index 81% rename from internal/util/test_setup.go rename to test_setup/test_setup.go index 532c464b..74a06abf 100644 --- a/internal/util/test_setup.go +++ b/test_setup/test_setup.go @@ -1,18 +1,19 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package util +package test_setup import ( "testing" "github.com/spf13/viper" "github.com/stretchr/testify/assert" + "github.com/twitchdev/twitch-cli/internal/util" ) func SetupTestEnv(t *testing.T) *assert.Assertions { assert := assert.New(t) - home, err := GetApplicationDir() + home, err := util.GetApplicationDir() assert.Nil(err) viper.AddConfigPath(home)