@typetron/framework 0.5.3 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/.eslintrc-old.js +208 -0
  2. package/Cache/Cache.ts +13 -0
  3. package/Cache/Stores/DatabaseStore.ts +120 -0
  4. package/Cache/Stores/FileStore.ts +69 -0
  5. package/Cache/Stores/MemoryStore.ts +71 -0
  6. package/Cache/index.ts +5 -0
  7. package/Cache/types.ts +4 -0
  8. package/Container/ClassResolver.ts +106 -0
  9. package/Container/Container.ts +144 -0
  10. package/Container/Contracts.ts +3 -0
  11. package/Container/Decorators.ts +20 -0
  12. package/Container/Metadata.ts +16 -0
  13. package/Container/Resolver.ts +27 -0
  14. package/Container/index.ts +5 -0
  15. package/Database/Clauses/Where.ts +0 -0
  16. package/Database/Connection.ts +52 -0
  17. package/Database/Decorators.ts +142 -0
  18. package/Database/Drivers/DatabaseDriver.ts +37 -0
  19. package/Database/Drivers/MySQL/Alter/index.ts +55 -0
  20. package/Database/Drivers/MySQL/ColumnDefinition.ts +16 -0
  21. package/Database/Drivers/MySQL/Create.ts +47 -0
  22. package/Database/Drivers/MySQL/MysqlDriver.ts +123 -0
  23. package/Database/Drivers/MySQL/Schema.ts +82 -0
  24. package/Database/Drivers/MySQL/index.ts +13 -0
  25. package/Database/Drivers/SQL/Schema.ts +73 -0
  26. package/Database/Drivers/SQL/Statements/Alter.ts +22 -0
  27. package/Database/Drivers/SQL/Statements/BaseStatement.ts +55 -0
  28. package/Database/Drivers/SQL/Statements/Delete.ts +20 -0
  29. package/Database/Drivers/SQL/Statements/Insert.ts +24 -0
  30. package/Database/Drivers/SQL/Statements/Select.ts +55 -0
  31. package/Database/Drivers/SQL/Statements/Update.ts +33 -0
  32. package/Database/Drivers/SQL/index.ts +8 -0
  33. package/Database/Drivers/SQLite/Create.ts +45 -0
  34. package/Database/Drivers/SQLite/Schema.ts +54 -0
  35. package/Database/Drivers/SQLite/SqliteDriver.ts +144 -0
  36. package/Database/Drivers/SQLite/index.ts +15 -0
  37. package/Database/Drivers/SchemaContract.ts +11 -0
  38. package/Database/Drivers/Statement.ts +16 -0
  39. package/Database/Drivers/index.ts +4 -0
  40. package/Database/Entity.ts +377 -0
  41. package/Database/EntityNotFoundError.ts +5 -0
  42. package/Database/EntityProxyHandler.ts +43 -0
  43. package/Database/EntityQuery.ts +197 -0
  44. package/Database/Expression.ts +3 -0
  45. package/Database/Fields.ts +816 -0
  46. package/Database/Helpers.ts +12 -0
  47. package/Database/List.ts +71 -0
  48. package/Database/Migrations/Migration.ts +10 -0
  49. package/Database/Migrations/MigrationHistory.ts +20 -0
  50. package/Database/Migrations/Migrator.ts +122 -0
  51. package/Database/Migrations/index.ts +3 -0
  52. package/Database/ORM/BaseRelationship.ts +10 -0
  53. package/Database/Query.ts +374 -0
  54. package/Database/Seeders/Seeder.ts +3 -0
  55. package/Database/Seeders/SeederManager.ts +27 -0
  56. package/Database/Seeders/index.ts +2 -0
  57. package/Database/StringExpression.ts +9 -0
  58. package/Database/Types.ts +165 -0
  59. package/Database/index.ts +23 -0
  60. package/Encryption/index.ts +51 -0
  61. package/Forms/Decorators.ts +29 -0
  62. package/Forms/Form.ts +70 -0
  63. package/Forms/FormFields.ts +36 -0
  64. package/Forms/index.ts +3 -0
  65. package/Framework/App.ts +10 -0
  66. package/Framework/Application.ts +118 -0
  67. package/Framework/Auth/Auth.ts +122 -0
  68. package/Framework/Auth/Authenticatable.ts +9 -0
  69. package/Framework/Auth/User.ts +27 -0
  70. package/Framework/Auth/index.ts +29 -0
  71. package/Framework/Config/AppConfig.ts +25 -0
  72. package/Framework/Config/AuthConfig.ts +9 -0
  73. package/Framework/Config/BaseConfig.ts +9 -0
  74. package/Framework/Config/CacheConfig.ts +38 -0
  75. package/Framework/Config/DatabaseConfig.ts +21 -0
  76. package/Framework/Config/MailConfig.ts +30 -0
  77. package/Framework/Config/index.ts +6 -0
  78. package/Framework/Middleware/AuthMiddleware.ts +31 -0
  79. package/Framework/Middleware/CorsMiddleware.ts +32 -0
  80. package/Framework/Middleware/StaticAssetsMiddleware.ts +76 -0
  81. package/Framework/Middleware/index.ts +2 -0
  82. package/Framework/Provider.ts +9 -0
  83. package/Framework/Providers/CacheProvider.ts +28 -0
  84. package/Framework/Providers/DatabaseProvider.ts +36 -0
  85. package/Framework/Providers/MailProvider.ts +31 -0
  86. package/Framework/Providers/WebsocketsProvider.ts +120 -0
  87. package/Framework/Resolvers/AuthResolver.ts +23 -0
  88. package/Framework/Resolvers/EntityResolver.ts +41 -0
  89. package/Framework/Resolvers/FormResolver.ts +37 -0
  90. package/Framework/RootDir.ts +1 -0
  91. package/Framework/index.ts +8 -0
  92. package/Frontend/Angular/index.ts +24 -0
  93. package/Frontend/Websockets/index.ts +86 -0
  94. package/Mail/MailAddress.ts +4 -0
  95. package/Mail/MailTransport.ts +6 -0
  96. package/Mail/Mailable.ts +16 -0
  97. package/Mail/Mailer.ts +27 -0
  98. package/Mail/PendingMail.ts +68 -0
  99. package/Mail/SentMessage.ts +20 -0
  100. package/Mail/Transports/MemoryTransport.ts +13 -0
  101. package/Mail/Transports/SendGridTransport.ts +95 -0
  102. package/Mail/index.ts +3 -0
  103. package/Models/Model.ts +41 -0
  104. package/Models/index.ts +54 -0
  105. package/Router/BaseController.ts +7 -0
  106. package/Router/Decorators.ts +224 -0
  107. package/Router/Guard.ts +10 -0
  108. package/Router/Http/Contracts.ts +4 -0
  109. package/Router/Http/ErrorHandler.ts +45 -0
  110. package/Router/Http/ErrorHandlerInterface.ts +6 -0
  111. package/Router/Http/Errors/HttpError.ts +15 -0
  112. package/Router/Http/Handler.ts +183 -0
  113. package/Router/Http/HttpRoute.ts +110 -0
  114. package/Router/Http/Middleware.ts +7 -0
  115. package/Router/Http/Request.ts +225 -0
  116. package/Router/Http/Response.ts +35 -0
  117. package/Router/Http/index.ts +85 -0
  118. package/Router/Metadata.ts +34 -0
  119. package/Router/Middleware.ts +16 -0
  120. package/Router/Request.ts +65 -0
  121. package/Router/Route.ts +16 -0
  122. package/Router/RouteNotFoundError.ts +5 -0
  123. package/Router/Router.ts +35 -0
  124. package/Router/Servers/index.ts +6 -0
  125. package/Router/Servers/node.ts +49 -0
  126. package/Router/Servers/uNetworking.ts +151 -0
  127. package/Router/Websockets/Handler.ts +82 -0
  128. package/Router/Websockets/Middleware.ts +7 -0
  129. package/Router/Websockets/Request.ts +3 -0
  130. package/Router/Websockets/WebsocketRoute.ts +61 -0
  131. package/Router/Websockets/index.ts +57 -0
  132. package/Router/Websockets/types.ts +23 -0
  133. package/Router/index.ts +6 -0
  134. package/Storage/File.ts +30 -0
  135. package/Storage/Image.ts +19 -0
  136. package/Storage/InvalidImage.ts +3 -0
  137. package/Storage/Storage.ts +98 -0
  138. package/Storage/index.ts +4 -0
  139. package/Storage/utils.ts +18 -0
  140. package/Support/Array.ts +184 -0
  141. package/Support/Math.ts +12 -0
  142. package/Support/Metadata.ts +31 -0
  143. package/Support/String.ts +38 -0
  144. package/Support/index.ts +19 -0
  145. package/Support/utils.ts +7 -0
  146. package/Testing/TestCase.ts +193 -0
  147. package/Validation/Rule.ts +9 -0
  148. package/Validation/RuleInterface.ts +10 -0
  149. package/Validation/Rules/Email.ts +20 -0
  150. package/Validation/Rules/FileExtension.ts +21 -0
  151. package/Validation/Rules/MaxLength.ts +17 -0
  152. package/Validation/Rules/MinLength.ts +18 -0
  153. package/Validation/Rules/Optional.ts +18 -0
  154. package/Validation/Rules/Required.ts +29 -0
  155. package/Validation/Validator.ts +7 -0
  156. package/Validation/index.ts +12 -0
  157. package/benchmark-index.js +20 -0
  158. package/benchmark.ts +47 -0
  159. package/package.json +7 -3
  160. package/tests/Cache/DatabaseStoreTest.ts +110 -0
  161. package/tests/Cache/FileStoreTest.ts +93 -0
  162. package/tests/Cache/MemoryStoreTest.ts +92 -0
  163. package/tests/Container/ContainerTest.ts +336 -0
  164. package/tests/Database/Drivers/MySQL/AlterTest.ts +100 -0
  165. package/tests/Database/Drivers/MySQL/CreateTest.ts +39 -0
  166. package/tests/Database/Drivers/MysqlDriverTest.ts +140 -0
  167. package/tests/Database/Drivers/SqliteDriverTest.ts +126 -0
  168. package/tests/Database/Entities/Article.ts +21 -0
  169. package/tests/Database/Entities/Profile.ts +21 -0
  170. package/tests/Database/Entities/Role.ts +15 -0
  171. package/tests/Database/Entities/User.ts +35 -0
  172. package/tests/Database/EntityRelationshipsTest.ts +475 -0
  173. package/tests/Database/EntityTest.ts +202 -0
  174. package/tests/Database/Migrations/RunnerTest.ts +96 -0
  175. package/tests/Database/Migrations/migrations/1.createUserTable.ts +12 -0
  176. package/tests/Database/Migrations/migrations/2.createArticlesTable.ts +12 -0
  177. package/tests/Database/QueryTest.ts +573 -0
  178. package/tests/Database/SchemaTest.ts +32 -0
  179. package/tests/Database/Seeders/RunnerTest.ts +30 -0
  180. package/tests/Database/Seeders/seeders/RandomSeeder.ts +10 -0
  181. package/tests/Database/utils.ts +16 -0
  182. package/tests/Forms/FormsTest.ts +59 -0
  183. package/tests/Framework/Middleware/StaticAssetsMiddlewareTest.ts +129 -0
  184. package/tests/Framework/Resolvers/EntityResolverTest.ts +48 -0
  185. package/tests/Framework/Router/RouterTest.ts +33 -0
  186. package/tests/Mail/MailerTest.ts +76 -0
  187. package/tests/Model/ModelTest.ts +183 -0
  188. package/tests/Router/HttpHandlerTest.ts +72 -0
  189. package/tests/Router/RequestTest.ts +77 -0
  190. package/tests/Router/WebSocketsHandlerTest.ts +51 -0
  191. package/tests/Storage/StorageTest.ts +97 -0
  192. package/tests/Support/ArrayTest.ts +167 -0
  193. package/tests/Support/StringTest.ts +18 -0
  194. package/tests/Validation/RulesTest.ts +54 -0
  195. package/types.d.ts +8 -0
@@ -0,0 +1,126 @@
1
+ import { suite, test } from '@testdeck/mocha'
2
+ import { BelongsTo, Column, Entity, HasOne, ID, Options, PrimaryColumn, Relation, SqliteDriver } from '@Typetron/Database'
3
+ import { anyString, instance, mock, when } from '@Typetron/node_modules/ts-mockito'
4
+ import { Schema } from '@Typetron/Database/Drivers/SQLite/Schema'
5
+ import { expectQuery } from '@Typetron/tests/Database/utils'
6
+ import { expect } from 'chai'
7
+ import { DatabaseDriver } from '@Typetron/Database/Drivers/DatabaseDriver'
8
+
9
+ @suite
10
+ class SqliteDriverTest {
11
+
12
+ driver: DatabaseDriver
13
+ schema: Schema
14
+ query: string
15
+
16
+ async before() {
17
+ this.driver = mock(SqliteDriver)
18
+ this.schema = new Schema(instance(this.driver))
19
+ when(this.driver.run(anyString())).thenCall((query: string) => this.query = query)
20
+ }
21
+
22
+ @test
23
+ async createsEmptyTable() {
24
+ @Options()
25
+ class Test extends Entity {}
26
+
27
+ await this.schema.synchronize([Test].pluck('metadata'))
28
+ expectQuery(this.query).toEqual('CREATE TABLE test ( )')
29
+ }
30
+
31
+ @test
32
+ async createsTableWithIntegerColumn() {
33
+ class Test extends Entity {
34
+ @Column()
35
+ age: number
36
+ }
37
+
38
+ await this.schema.synchronize([Test].pluck('metadata'))
39
+ expectQuery(this.query).toEqual('CREATE TABLE test ( age integer )')
40
+ }
41
+
42
+ @test
43
+ async createsTableWithPrimaryAutoIncrementColumn() {
44
+ class Test extends Entity {
45
+ @PrimaryColumn()
46
+ id: ID
47
+ }
48
+
49
+ await this.schema.synchronize([Test].pluck('metadata'))
50
+ expectQuery(this.query).toEqual(`
51
+ CREATE TABLE test
52
+ (
53
+ id integer PRIMARY KEY AUTOINCREMENT
54
+ )
55
+ `)
56
+ }
57
+
58
+ @test
59
+ async createsTableWithForeignColumn() {
60
+ class User extends Entity {
61
+ @Column()
62
+ @Relation(() => Image, 'user')
63
+ image: HasOne<Image>
64
+ }
65
+
66
+ class Image extends Entity {
67
+ @Column()
68
+ @Relation(() => User, 'image')
69
+ user: BelongsTo<User>
70
+ }
71
+
72
+ await this.schema.synchronize([Image].pluck('metadata'))
73
+ expectQuery(this.query).toEqual(`
74
+ CREATE TABLE image
75
+ (
76
+ userId integer
77
+ )
78
+ `)
79
+ }
80
+
81
+ @test
82
+ async altersTable() {
83
+ when(this.driver.tableExists(anyString())).thenReturn(Promise.resolve(true))
84
+ when(this.driver.tableColumns(anyString())).thenReturn(Promise.resolve([
85
+ {
86
+ name: 'column2',
87
+ type: 'integer'
88
+ },
89
+ {
90
+ name: 'column3',
91
+ type: 'integer'
92
+ },
93
+ ]))
94
+
95
+ const queries: string[] = []
96
+ when(this.driver.run(anyString())).thenCall((query: string) => queries.push(query))
97
+
98
+ class User extends Entity {
99
+ @Column()
100
+ column1: number
101
+
102
+ @Column()
103
+ column2: string
104
+ }
105
+
106
+ await this.schema.synchronize([User].pluck('metadata'))
107
+
108
+ expect(queries).to.have.length(4)
109
+ expectQuery(queries[0]).toEqual(`
110
+ CREATE TABLE user_alter_tmp
111
+ (
112
+ column1 integer,
113
+ column2 varchar(255)
114
+ )
115
+ `)
116
+
117
+ expectQuery(queries[1]).toEqual(`
118
+ INSERT INTO user_alter_tmp(column2)
119
+ SELECT column2
120
+ FROM user
121
+ `)
122
+
123
+ expectQuery(queries[2]).toEqual(`DROP TABLE user`)
124
+ expectQuery(queries[3]).toEqual(`ALTER TABLE user_alter_tmp RENAME TO user`)
125
+ }
126
+ }
@@ -0,0 +1,21 @@
1
+ import { Column, Entity, PrimaryColumn, Relation } from '../../../Database'
2
+ import { User } from './User'
3
+ import { BelongsTo } from '../../../Database/Fields'
4
+
5
+ export class Article extends Entity {
6
+
7
+ @PrimaryColumn()
8
+ id: number
9
+
10
+ @Column()
11
+ title: string
12
+
13
+ @Column()
14
+ content: string
15
+
16
+ @Column()
17
+ published: boolean = false
18
+
19
+ @Relation(() => User, 'articles')
20
+ author: BelongsTo<User>
21
+ }
@@ -0,0 +1,21 @@
1
+ import { Column, CreatedAt, Entity, PrimaryColumn, Relation, UpdatedAt } from '../../../Database'
2
+ import { User } from './User'
3
+ import { BelongsTo } from '../../../Database/Fields'
4
+
5
+ export class Profile extends Entity {
6
+
7
+ @PrimaryColumn()
8
+ id: number
9
+
10
+ @Relation(() => User, 'profile')
11
+ user: BelongsTo<User>
12
+
13
+ @Column()
14
+ address: string
15
+
16
+ @CreatedAt()
17
+ createdAt: Date
18
+
19
+ @UpdatedAt()
20
+ updatedAt: Date
21
+ }
@@ -0,0 +1,15 @@
1
+ import { Column, Entity, PrimaryColumn, Relation } from '../../../Database'
2
+ import { User } from './User'
3
+ import { BelongsToMany } from '../../../Database/Fields'
4
+
5
+ export class Role extends Entity {
6
+
7
+ @PrimaryColumn()
8
+ id: number
9
+
10
+ @Column()
11
+ name: string
12
+
13
+ @Relation(() => User, 'roles')
14
+ users: BelongsToMany<User>
15
+ }
@@ -0,0 +1,35 @@
1
+ import { Column, CreatedAt, Entity, Options, PrimaryColumn, Relation, UpdatedAt } from '../../../Database'
2
+ import { Article } from './Article'
3
+ import { Role } from './Role'
4
+ import { Profile } from './Profile'
5
+ import { BelongsToMany, HasMany, HasOne } from '../../../Database/Fields'
6
+
7
+ @Options({
8
+ table: 'users'
9
+ })
10
+ export class User extends Entity {
11
+
12
+ @PrimaryColumn()
13
+ id: number
14
+
15
+ @Column()
16
+ name: string
17
+
18
+ @Column()
19
+ email: string
20
+
21
+ @Relation(() => Profile, 'user')
22
+ profile: HasOne<Profile>
23
+
24
+ @Relation(() => Article, 'author')
25
+ articles: HasMany<Article>
26
+
27
+ @Relation(() => Role, 'users')
28
+ roles: BelongsToMany<Role>
29
+
30
+ @CreatedAt()
31
+ createdAt: Date
32
+
33
+ @UpdatedAt()
34
+ updatedAt: Date
35
+ }
@@ -0,0 +1,475 @@
1
+ import { suite, test } from '@testdeck/mocha'
2
+ import { Connection, Entity, EntityConstructor, Query, SqliteDriver } from '../../Database'
3
+ import { User } from './Entities/User'
4
+ import { Article } from './Entities/Article'
5
+ import { Role } from './Entities/Role'
6
+ import { Profile } from './Entities/Profile'
7
+ import { expect } from 'chai'
8
+ import { BelongsTo, BelongsToMany, HasMany, HasOne } from '../../Database/Fields'
9
+
10
+ @suite
11
+ class EntityRelationshipsTest {
12
+ joe = {
13
+ name: 'Joe',
14
+ email: 'joe@example.com',
15
+ }
16
+
17
+ doe = {
18
+ name: 'Doe',
19
+ email: 'doe@example.com',
20
+ }
21
+
22
+ admin = {
23
+ name: 'Admin'
24
+ }
25
+ developer = {
26
+ name: 'Developer'
27
+ }
28
+
29
+ async before() {
30
+ Query.connection = new Connection(new SqliteDriver(':memory:'))
31
+ // Query.connection = new Connection(new MysqlDriver({
32
+ // host: 'localhost', user: 'root', password: 'root', database: 'typetron_test'
33
+ // }))
34
+ await Query.connection.driver.schema.synchronize([User, Profile, Article, Role].pluck('metadata'))
35
+ }
36
+
37
+ async after() {
38
+ for await (const entity of [User, Profile, Article, Role]) {
39
+ await (entity as EntityConstructor<Entity>).truncate()
40
+ }
41
+
42
+ await Query.table('role_users').truncate()
43
+ }
44
+
45
+ @test
46
+ async hydratesEntityWithRelationships() {
47
+ const user = new User()
48
+
49
+ expect(user).to.haveOwnProperty('profile')
50
+ expect(user.profile).to.be.instanceOf(HasOne)
51
+ expect(user).to.haveOwnProperty('articles')
52
+ expect(user.articles).to.be.instanceOf(HasMany)
53
+ expect(user).to.haveOwnProperty('roles')
54
+ expect(user.roles).to.be.instanceOf(BelongsToMany)
55
+ }
56
+
57
+ @test
58
+ async hasOneSave() {
59
+ const user = await User.create(this.joe)
60
+
61
+ const profile = new Profile().fill({
62
+ address: 'address'
63
+ })
64
+ await user.profile.save(profile)
65
+
66
+ expect(user.profile.get()?.id).to.be.equal(profile.id)
67
+ expect(user.profile.get()?.user.get()?.id).to.be.equal(user.id)
68
+ expect(await Profile.first()).to.be.instanceOf(Profile)
69
+ }
70
+
71
+ @test
72
+ async hasOneLoad() {
73
+ const user = await User.create(this.joe)
74
+
75
+ const profile = new Profile().fill({
76
+ address: 'address'
77
+ })
78
+ await user.profile.save(profile)
79
+
80
+ const sameUser = await User.findOrFail(user.id)
81
+ expect(sameUser.profile).to.be.instanceOf(HasOne)
82
+ expect(sameUser.profile.get()).to.be.equal(undefined)
83
+ await sameUser.load('profile')
84
+
85
+ expect(sameUser.profile.get()?.id).to.be.equal(user.profile.get()?.id)
86
+ expect(sameUser.profile.get()?.address).to.be.equal('address')
87
+ }
88
+
89
+ @test
90
+ async hasOneEagerLoad() {
91
+ const user = await User.create(this.joe)
92
+
93
+ const profile = new Profile().fill({
94
+ address: 'address'
95
+ })
96
+ await user.profile.save(profile)
97
+
98
+ const sameUser = await User.with('profile').where('id', user.id).first() as User
99
+ expect(sameUser.profile.get()).to.deep.include({
100
+ id: profile.id,
101
+ address: 'address'
102
+ })
103
+ }
104
+
105
+ @test
106
+ async hasManySave() {
107
+ const user = await User.create(this.joe)
108
+
109
+ const article = new Article().fill({
110
+ title: 'title',
111
+ content: 'content',
112
+ })
113
+ await user.articles.save(article)
114
+
115
+ expect(user.articles).to.have.length(1)
116
+ expect(user.articles[0]).to.deep.include({
117
+ id: article.id,
118
+ title: 'title',
119
+ content: 'content',
120
+ })
121
+ }
122
+
123
+ @test
124
+ async hasManySaveUsingPlainObject() {
125
+ const user = await User.create(this.joe)
126
+
127
+ await user.articles.save({
128
+ title: 'title',
129
+ content: 'content',
130
+ } as Article)
131
+
132
+ expect(user.articles).to.have.length(1)
133
+ expect(user.articles[0]).to.deep.include({
134
+ title: 'title',
135
+ content: 'content',
136
+ })
137
+ }
138
+
139
+ @test
140
+ async hasManySaveUsingSaveOnUnsavedParent() {
141
+ const user = new User().fill(this.joe)
142
+
143
+ await user.articles.save(new Article().fill({
144
+ title: 'title',
145
+ content: 'content',
146
+ }))
147
+
148
+ expect(user.articles).to.have.length(1)
149
+ expect(user.articles[0]).to.deep.include({
150
+ title: 'title',
151
+ content: 'content',
152
+ })
153
+ }
154
+
155
+ @test
156
+ async hasManyLoad() {
157
+ const user = await User.create(this.joe)
158
+ await Article.create({
159
+ title: 'title',
160
+ content: 'content',
161
+ author: user
162
+ })
163
+
164
+ await user.load('articles')
165
+ expect(user.articles).to.be.instanceOf(HasMany)
166
+ expect(user.articles[0]).to.deep.include({
167
+ title: 'title',
168
+ content: 'content',
169
+ })
170
+ }
171
+
172
+ @test
173
+ async hasManyGet() {
174
+ const user = await User.create(this.joe)
175
+ const article = new Article().fill({
176
+ title: 'title',
177
+ content: 'content',
178
+ })
179
+ await user.articles.save(article)
180
+ await user.articles.load()
181
+ expect(user.articles[0]).to.deep.include({
182
+ title: 'title',
183
+ content: 'content',
184
+ })
185
+ }
186
+
187
+ @test
188
+ async hasManyEagerLoad() {
189
+ const user = new User().fill(this.joe)
190
+ const article = new Article().fill({
191
+ title: 'title',
192
+ content: 'content',
193
+ })
194
+ await user.articles.save(article)
195
+
196
+ const sameUser = await User.with('articles').where('id', user.id).first() as User
197
+ expect(sameUser.articles).to.have.length(1)
198
+ expect(user.articles[0]).to.deep.include({
199
+ id: article.id,
200
+ title: 'title',
201
+ content: 'content',
202
+ })
203
+ }
204
+
205
+ @test
206
+ async hasManyQuery() {
207
+ const user = await User.create(this.joe)
208
+ await user.articles.saveMany(
209
+ {
210
+ title: 'title',
211
+ content: 'joe\'s content',
212
+ },
213
+ {
214
+ title: 'other title',
215
+ content: 'other content',
216
+ },
217
+ )
218
+ const doe = await User.create(this.doe)
219
+ await doe.articles.save({
220
+ title: 'title',
221
+ content: 'doe\'s content',
222
+ })
223
+
224
+ const articles = await user.articles.where('title', 'title').get()
225
+ expect(articles).to.have.length(1)
226
+ expect(user.articles[0]).to.deep.include({
227
+ title: 'title',
228
+ content: 'joe\'s content',
229
+ })
230
+ }
231
+
232
+ @test
233
+ async belongsToSave() {
234
+ const user = await User.create(this.joe)
235
+ const article = new Article()
236
+ await user.articles.save(article)
237
+
238
+ expect(user.articles).to.have.length(1)
239
+ expect(user.articles[0].author).to.be.instanceof(BelongsTo)
240
+ }
241
+
242
+ @test
243
+ async belongsToEagerLoad() {
244
+ const user = await User.create(this.joe)
245
+ const article = new Article()
246
+ await user.articles.save(article)
247
+
248
+ const articles = await Article.with('author').get()
249
+
250
+ expect(articles).to.have.length(1)
251
+ expect(articles[0].author).to.be.instanceof(BelongsTo)
252
+ }
253
+
254
+ @test
255
+ async belongsToEagerLoadWithCustomQuery() {
256
+ const joe = await User.create(this.joe)
257
+ await joe.articles.save(Article.new({title: 'article1'}))
258
+ await joe.articles.save(Article.new({title: 'article2'}))
259
+
260
+ const user = await User.with(['articles', query => query.where('title', 'article1')]).first() as User
261
+
262
+ expect(user.articles).to.have.length(1)
263
+ }
264
+
265
+ // @test
266
+ // async deepBelongsToEagerLoadWithCustomQuery() {
267
+ // const admin = await Role.create(this.admin)
268
+ //
269
+ // const joe = await User.create(this.joe)
270
+ // await joe.profile.save(new Profile({address: 'south address'}))
271
+ // const doe = await User.create(this.doe)
272
+ // await doe.profile.save(new Profile({address: 'east address'}))
273
+ //
274
+ // await admin.users.add(joe, doe)
275
+ //
276
+ // const role = await Role.with(['users.profile', query => query.whereLike('address', '%east%')]).first() as Role
277
+ //
278
+ // expect(role.users).to.have.length(1)
279
+ // expect(role.users[0].name).to.be.equal(doe.name)
280
+ // }
281
+
282
+ @test
283
+ async belongsToLoad() {
284
+ const user = await User.create(this.joe)
285
+ let article = new Article()
286
+ await user.articles.save(article)
287
+
288
+ article = await Article.findOrFail(article.id)
289
+ const a = await Article.get()
290
+ const u = await User.get()
291
+ await article.load('author')
292
+
293
+ expect(article.author).to.be.instanceOf(BelongsTo)
294
+ expect(article.author.get()).to.be.instanceOf(User)
295
+ }
296
+
297
+ @test
298
+ async belongsToReLoad() {
299
+ const user = await User.create(this.joe)
300
+ const article = new Article()
301
+ await user.articles.save(article)
302
+
303
+ await article.load('author')
304
+
305
+ expect(article.author).to.be.instanceOf(BelongsTo)
306
+ expect(article.author.get()).to.be.instanceOf(User)
307
+ expect(article.author.get()?.id).to.be.equal(user.id)
308
+ }
309
+
310
+ @test
311
+ async belongsToManySave() {
312
+ const user = new User().fill(this.joe)
313
+
314
+ const admin = await Role.create(this.admin)
315
+ const developer = await Role.create(this.developer)
316
+ await user.roles.add(admin.id, developer.id)
317
+
318
+ expect(user.roles).to.have.length(2)
319
+ expect(user.roles[0].id).to.be.equal(admin.id)
320
+ expect(user.roles[1].id).to.be.equal(developer.id)
321
+ }
322
+
323
+ @test
324
+ async belongsToManyEagerLoad() {
325
+ const user = await User.create(this.joe)
326
+
327
+ const admin = await Role.create(this.admin)
328
+ const developer = await Role.create(this.developer)
329
+ await user.roles.add(admin.id, developer.id)
330
+
331
+ const sameUser = await User.with('roles').where('id', user.id).first() as User
332
+
333
+ expect(sameUser.roles).to.have.length(2)
334
+ expect(sameUser.roles[0]).to.deep.include({
335
+ id: admin.id,
336
+ name: admin.name
337
+ })
338
+ expect(sameUser.roles[1]).to.deep.include({
339
+ id: developer.id,
340
+ name: developer.name
341
+ })
342
+ }
343
+
344
+ @test
345
+ async belongsToManyClearing() {
346
+ const user = new User().fill(this.joe)
347
+ const admin = await Role.create(this.admin)
348
+ const developer = await Role.create(this.developer)
349
+ await user.roles.add(admin.id, developer.id)
350
+
351
+ expect(user.roles).to.have.length(2)
352
+ await user.roles.clear()
353
+ expect(user.roles).to.have.length(0)
354
+ }
355
+
356
+ @test
357
+ async belongsToManyRemove() {
358
+ const user = new User().fill(this.joe)
359
+ const admin = await Role.create(this.admin)
360
+ const developer = await Role.create(this.developer)
361
+ await user.roles.add(admin.id, developer.id)
362
+
363
+ expect(user.roles).to.have.length(2)
364
+ await user.roles.remove(admin.id)
365
+ expect(user.roles).to.have.length(1)
366
+ }
367
+
368
+ @test
369
+ async belongsToManySync() {
370
+ const user = new User().fill(this.joe)
371
+ const admin = await Role.create(this.admin)
372
+ const developer = await Role.create(this.developer)
373
+ const manager = await Role.create({name: 'Manager'})
374
+ await user.roles.add(admin.id, manager.id)
375
+
376
+ await user.roles.sync(admin.id, developer)
377
+ expect(user.roles).to.have.length(2)
378
+ expect(user.roles[0]).to.have.property('id', admin.id)
379
+ expect(user.roles[1]).to.have.property('id', developer.id)
380
+ }
381
+
382
+ @test
383
+ async belongsToManySyncWithoutDetaching() {
384
+ const user = new User().fill(this.joe)
385
+ const admin = await Role.create(this.admin)
386
+ const developer = await Role.create(this.developer)
387
+ const manager = await Role.create({name: 'manager'})
388
+ await user.roles.add(admin.id, manager.id)
389
+
390
+ await user.roles.syncWithoutDetaching(admin.id, developer)
391
+ expect(user.roles).to.have.length(3)
392
+ expect(user.roles[0]).to.have.property('id', admin.id)
393
+ expect(user.roles[1]).to.have.property('id', manager.id)
394
+ expect(user.roles[2]).to.have.property('id', developer.id)
395
+ }
396
+
397
+ @test
398
+ async belongsToManyToggle() {
399
+ const user = new User().fill(this.joe)
400
+ const admin = await Role.create(this.admin)
401
+ await user.roles.add(admin.id)
402
+
403
+ expect(user.roles).to.have.length(1)
404
+ expect(user.roles[0]).to.have.property('id', admin.id)
405
+
406
+ const developer = await Role.create(this.developer)
407
+ await user.roles.toggle(admin.id, developer.id)
408
+
409
+ expect(user.roles).to.have.length(1)
410
+ expect(user.roles[0]).to.have.property('id', developer.id)
411
+ }
412
+
413
+ @test
414
+ async belongsToManyLoad() {
415
+ const user = new User().fill(this.joe)
416
+ const admin = await Role.create(this.admin)
417
+ await user.roles.add(admin.id)
418
+
419
+ const sameUser = await User.first() as User
420
+ await sameUser.load('roles')
421
+ expect(sameUser.roles[0]).to.deep.include(this.admin)
422
+ }
423
+
424
+ @test
425
+ async belongsToManyQuery() {
426
+ const user = new User().fill(this.joe)
427
+ const admin = await Role.create(this.admin)
428
+ const developer = await Role.create(this.developer)
429
+ await user.roles.add(admin.id, developer.id)
430
+
431
+ expect(user.roles).to.have.length(2)
432
+ const roles = await user.roles.where('name', developer.name).get()
433
+ expect(roles).to.have.length(1)
434
+ expect(roles[0]).to.deep.include(this.developer)
435
+ }
436
+
437
+ @test
438
+ async belongsToManyHas() {
439
+ const user = new User().fill(this.joe)
440
+ const admin = await Role.create(this.admin)
441
+ const developer = await Role.create(this.developer)
442
+ await user.roles.add(admin.id)
443
+
444
+ expect(await user.roles.has(admin)).to.be.equal(true)
445
+ expect(await user.roles.has(developer.id)).to.be.equal(false)
446
+ }
447
+
448
+ @test
449
+ async nestedEagerLoading() {
450
+ let user = await User.create(this.joe)
451
+ await user.articles.save(new Article())
452
+
453
+ user = await User.find(user.id) as User
454
+ await user.load('articles.author')
455
+
456
+ expect(user.articles[0].author.get()?.name).to.be.equal(user.name)
457
+ }
458
+
459
+ @test
460
+ async multipleNestedEagerLoading() {
461
+ let adminRole = await Role.create({name: 'Admin'})
462
+ const joe = await User.create(this.joe)
463
+ await adminRole.users.add(joe)
464
+ await joe.articles.save(new Article().fill({title: 'title'}))
465
+ await joe.profile.save(new Profile().fill({address: 'address'}))
466
+
467
+ adminRole = await Role.find(adminRole.id) as Role
468
+ await adminRole.load('users.articles.author', 'users.profile.user')
469
+
470
+ expect(adminRole.users[0].articles[0].title).to.be.equal('title')
471
+ expect(adminRole.users[0].profile.get()?.address).to.be.equal('address')
472
+ expect(adminRole.users[0].articles[0].author.get()?.name).to.be.equal(joe.name)
473
+ expect(adminRole.users[0].profile.get()?.user.get()?.name).to.be.equal(joe.name)
474
+ }
475
+ }