Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / tests / projects.py @ fb619437

History | View | Annotate | Download (30.8 kB)

1
# Copyright 2011, 2012, 2013 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
from astakos.im.tests.common import *
35

    
36

    
37
class ProjectAPITest(TestCase):
38

    
39
    def setUp(self):
40
        self.client = Client()
41
        component1 = Component.objects.create(name="comp1")
42
        register.add_service(component1, "service1", "type1", [])
43
        # custom service resources
44
        resource11 = {"name": "service1.resource11",
45
                      "desc": "resource11 desc",
46
                      "service_type": "type1",
47
                      "service_origin": "service1",
48
                      "ui_visible": True}
49
        r, _ = register.add_resource(resource11)
50
        register.update_resources([(r, 100)])
51
        resource12 = {"name": "service1.resource12",
52
                      "desc": "resource11 desc",
53
                      "service_type": "type1",
54
                      "service_origin": "service1",
55
                      "unit": "bytes"}
56
        r, _ = register.add_resource(resource12)
57
        register.update_resources([(r, 1024)])
58

    
59
        # create user
60
        self.user1 = get_local_user("test@grnet.gr", moderated=True)
61
        quotas.qh_sync_user(self.user1)
62
        self.user2 = get_local_user("test2@grnet.gr", moderated=True)
63
        self.user2.uuid = "uuid2"
64
        self.user2.save()
65
        quotas.qh_sync_user(self.user2)
66
        self.user3 = get_local_user("test3@grnet.gr", moderated=True)
67
        quotas.qh_sync_user(self.user3)
68

    
69
        astakos = Component.objects.create(name="astakos")
70
        register.add_service(astakos, "astakos_account", "account", [])
71
        # create another service
72
        pending_app = {"name": "astakos.pending_app",
73
                       "desc": "pend app desc",
74
                       "service_type": "account",
75
                       "service_origin": "astakos_account",
76
                       "ui_visible": False,
77
                       "api_visible": False}
78
        r, _ = register.add_resource(pending_app)
79
        register.update_resources([(r, 3)])
80
        accepted = AstakosUser.objects.accepted()
81
        quotas.update_base_quota(accepted, r.name, 3)
82

    
83
    def create(self, app, headers):
84
        dump = json.dumps(app)
85
        r = self.client.post(reverse("api_projects"), dump,
86
                             content_type="application/json", **headers)
87
        body = json.loads(r.content)
88
        return r.status_code, body
89

    
90
    def modify(self, app, project_id, headers):
91
        dump = json.dumps(app)
92
        kwargs = {"project_id": project_id}
93
        r = self.client.post(reverse("api_project", kwargs=kwargs), dump,
94
                             content_type="application/json", **headers)
95
        body = json.loads(r.content)
96
        return r.status_code, body
97

    
98
    def project_action(self, project_id, action, headers):
99
        action = json.dumps({action: "reason"})
100
        r = self.client.post(reverse("api_project_action",
101
                                     kwargs={"project_id": project_id}),
102
                             action, content_type="application/json",
103
                             **headers)
104
        return r.status_code
105

    
106
    def app_action(self, app_id, action, headers):
107
        action = json.dumps({action: "reason"})
108
        r = self.client.post(reverse("api_application_action",
109
                                     kwargs={"app_id": app_id}),
110
                             action, content_type="application/json",
111
                             **headers)
112
        return r.status_code
113

    
114
    def memb_action(self, memb_id, action, headers):
115
        action = json.dumps({action: "reason"})
116
        r = self.client.post(reverse("api_membership_action",
117
                                     kwargs={"memb_id": memb_id}), action,
118
                             content_type="application/json", **headers)
119
        return r.status_code
120

    
121
    def join(self, project_id, headers):
122
        action = {"join": {"project": project_id}}
123
        req = json.dumps(action)
124
        r = self.client.post(reverse("api_memberships"), req,
125
                             content_type="application/json", **headers)
126
        body = json.loads(r.content)
127
        return r.status_code, body
128

    
129
    def enroll(self, project_id, user, headers):
130
        action = {
131
            "enroll": {
132
                "project": project_id,
133
                "user": user.email,
134
            }
135
        }
136
        req = json.dumps(action)
137
        r = self.client.post(reverse("api_memberships"), req,
138
                             content_type="application/json", **headers)
139
        body = json.loads(r.content)
140
        return r.status_code, body
141

    
142
    @im_settings(PROJECT_ADMINS=["uuid2"])
143
    def test_projects(self):
144
        client = self.client
145
        h_owner = {"HTTP_X_AUTH_TOKEN": self.user1.auth_token}
146
        h_admin = {"HTTP_X_AUTH_TOKEN": self.user2.auth_token}
147
        h_plain = {"HTTP_X_AUTH_TOKEN": self.user3.auth_token}
148
        r = client.get(reverse("api_project", kwargs={"project_id": 1}))
149
        self.assertEqual(r.status_code, 401)
150

    
151
        r = client.get(reverse("api_project", kwargs={"project_id": 1}),
152
                       **h_owner)
153
        self.assertEqual(r.status_code, 404)
154
        r = client.get(reverse("api_application", kwargs={"app_id": 1}),
155
                       **h_owner)
156
        self.assertEqual(r.status_code, 404)
157
        r = client.get(reverse("api_membership", kwargs={"memb_id": 1}),
158
                       **h_owner)
159
        self.assertEqual(r.status_code, 404)
160

    
161
        status = self.memb_action(1, "accept", h_admin)
162
        self.assertEqual(status, 404)
163

    
164
        app1 = {"name": "test.pr",
165
                "end_date": "2013-5-5T20:20:20Z",
166
                "join_policy": "auto",
167
                "max_members": 5,
168
                "resources": {"service1.resource11": {
169
                    "member_capacity": 512}}
170
                }
171

    
172
        status, body = self.modify(app1, 1, h_owner)
173
        self.assertEqual(status, 404)
174

    
175
        # Create
176
        status, body = self.create(app1, h_owner)
177
        self.assertEqual(status, 201)
178
        project_id = body["id"]
179
        app_id = body["application"]
180

    
181
        # Get project
182
        r = client.get(reverse("api_project",
183
                               kwargs={"project_id": project_id}),
184
                       **h_owner)
185
        self.assertEqual(r.status_code, 200)
186
        body = json.loads(r.content)
187
        self.assertEqual(body["id"], project_id)
188
        self.assertEqual(body["application"], app_id)
189
        self.assertEqual(body["state"], "pending")
190
        self.assertEqual(body["owner"], self.user1.uuid)
191

    
192
        # Approve forbidden
193
        status = self.app_action(app_id, "approve", h_owner)
194
        self.assertEqual(status, 403)
195

    
196
        # Create another with the same name
197
        status, body = self.create(app1, h_owner)
198
        self.assertEqual(status, 201)
199
        project2_id = body["id"]
200
        project2_app_id = body["application"]
201

    
202
        # Create yet another, with different name
203
        app_p3 = copy.deepcopy(app1)
204
        app_p3["name"] = "new.pr"
205
        status, body = self.create(app_p3, h_owner)
206
        self.assertEqual(status, 201)
207
        project3_app_id = body["application"]
208

    
209
        # No more pending allowed
210
        status, body = self.create(app_p3, h_owner)
211
        self.assertEqual(status, 409)
212

    
213
        # Cancel
214
        status = self.app_action(project3_app_id, "cancel", h_owner)
215
        self.assertEqual(status, 200)
216

    
217
        # Modify
218
        app2 = {"name": "test.pr",
219
                "start_date": "2013-5-5T20:20:20Z",
220
                "end_date": "2013-7-5T20:20:20Z",
221
                "join_policy": "moderated",
222
                "leave_policy": "auto",
223
                "max_members": 3,
224
                "resources": {"service1.resource11": {
225
                    "member_capacity": 1024}}
226
                }
227

    
228
        status, body = self.modify(app2, project_id, h_owner)
229
        self.assertEqual(status, 201)
230
        self.assertEqual(project_id, body["id"])
231
        app2_id = body["application"]
232
        assertGreater(app2_id, app_id)
233

    
234
        # Dismiss failed
235
        status = self.app_action(app2_id, "dismiss", h_owner)
236
        self.assertEqual(status, 409)
237

    
238
        # Deny
239
        status = self.app_action(app2_id, "deny", h_admin)
240
        self.assertEqual(status, 200)
241

    
242
        r = client.get(reverse("api_application", kwargs={"app_id": app2_id}),
243
                       **h_owner)
244
        body = json.loads(r.content)
245
        self.assertEqual(body["state"], "denied")
246

    
247
        # Dismiss
248
        status = self.app_action(app2_id, "dismiss", h_owner)
249
        self.assertEqual(status, 200)
250

    
251
        # Resubmit
252
        status, body = self.modify(app2, project_id, h_owner)
253
        self.assertEqual(status, 201)
254
        app3_id = body["application"]
255

    
256
        # Approve
257
        status = self.app_action(app3_id, "approve", h_admin)
258
        self.assertEqual(status, 200)
259

    
260
        # Get related apps
261
        req = {"body": json.dumps({"project": project_id})}
262
        r = client.get(reverse("api_applications"), req, **h_owner)
263
        self.assertEqual(r.status_code, 200)
264
        body = json.loads(r.content)
265
        self.assertEqual(len(body), 3)
266

    
267
        # Get apps
268
        r = client.get(reverse("api_applications"), **h_owner)
269
        self.assertEqual(r.status_code, 200)
270
        body = json.loads(r.content)
271
        self.assertEqual(len(body), 5)
272

    
273
        # Enroll
274
        status, body = self.enroll(project_id, self.user3, h_owner)
275
        self.assertEqual(status, 200)
276
        m_plain_id = body["id"]
277

    
278
        # Join
279
        status, body = self.join(project_id, h_owner)
280
        self.assertEqual(status, 200)
281
        memb_id = body["id"]
282

    
283
        # Check memberships
284
        r = client.get(reverse("api_memberships"), **h_plain)
285
        body = json.loads(r.content)
286
        self.assertEqual(len(body), 1)
287
        m = body[0]
288
        self.assertEqual(m["user"], self.user3.uuid)
289
        self.assertEqual(m["state"], "accepted")
290

    
291
        r = client.get(reverse("api_memberships"), **h_owner)
292
        body = json.loads(r.content)
293
        self.assertEqual(len(body), 2)
294

    
295
        # Check membership
296
        r = client.get(reverse("api_membership", kwargs={"memb_id": memb_id}),
297
                       **h_admin)
298
        m = json.loads(r.content)
299
        self.assertEqual(m["user"], self.user1.uuid)
300
        self.assertEqual(m["state"], "requested")
301
        self.assertEqual(sorted(m["allowed_actions"]),
302
                         ["accept", "cancel", "reject"])
303

    
304
        r = client.get(reverse("api_membership", kwargs={"memb_id": memb_id}),
305
                       **h_plain)
306
        self.assertEqual(r.status_code, 403)
307

    
308
        status = self.memb_action(memb_id, "leave", h_admin)
309
        self.assertEqual(status, 409)
310

    
311
        status = self.memb_action(memb_id, "cancel", h_owner)
312
        self.assertEqual(status, 200)
313

    
314
        status, body = self.join(project_id, h_owner)
315
        self.assertEqual(status, 200)
316
        self.assertEqual(memb_id, body["id"])
317

    
318
        status = self.memb_action(memb_id, "reject", h_owner)
319
        self.assertEqual(status, 200)
320

    
321
        status, body = self.join(project_id, h_owner)
322
        self.assertEqual(status, 200)
323
        self.assertEqual(memb_id, body["id"])
324

    
325
        status = self.memb_action(memb_id, "accept", h_owner)
326
        self.assertEqual(status, 200)
327

    
328
        # Enroll fails, already in
329
        status, body = self.enroll(project_id, self.user1, h_owner)
330
        self.assertEqual(status, 409)
331

    
332
        # Remove member
333
        status = self.memb_action(memb_id, "remove", h_owner)
334
        self.assertEqual(status, 200)
335

    
336
        # Enroll a removed member
337
        status, body = self.enroll(project_id, self.user1, h_owner)
338
        self.assertEqual(status, 200)
339

    
340
        # Remove member
341
        status = self.memb_action(memb_id, "remove", h_owner)
342
        self.assertEqual(status, 200)
343

    
344
        # Re-join
345
        status, body = self.join(project_id, h_owner)
346
        self.assertEqual(status, 200)
347
        self.assertEqual(memb_id, body["id"])
348

    
349
        # Enroll a requested member
350
        status, body = self.enroll(project_id, self.user1, h_owner)
351
        self.assertEqual(status, 200)
352

    
353
        # Enroll fails, already in
354
        status, body = self.enroll(project_id, self.user1, h_owner)
355
        self.assertEqual(status, 409)
356

    
357
        # Enroll fails, project does not exist
358
        status, body = self.enroll(-1, self.user1, h_owner)
359
        self.assertEqual(status, 409)
360

    
361
        # Get projects
362
        ## Simple user mode
363
        r = client.get(reverse("api_projects"), **h_plain)
364
        body = json.loads(r.content)
365
        self.assertEqual(len(body), 1)
366
        p = body[0]
367
        with assertRaises(KeyError):
368
            p["pending_application"]
369

    
370
        ## Owner mode
371
        filters = {"filter": {"state": ["active", "cancelled"]}}
372
        req = {"body": json.dumps(filters)}
373
        r = client.get(reverse("api_projects"), req, **h_owner)
374
        body = json.loads(r.content)
375
        self.assertEqual(len(body), 2)
376
        assertIn("pending_application", body[0])
377

    
378
        filters = {"filter": {"state": "pending"}}
379
        req = {"body": json.dumps(filters)}
380
        r = client.get(reverse("api_projects"), req, **h_owner)
381
        body = json.loads(r.content)
382
        self.assertEqual(len(body), 1)
383
        self.assertEqual(body[0]["id"], project2_id)
384

    
385
        filters = {"filter": {"name": "test.pr"}}
386
        req = {"body": json.dumps(filters)}
387
        r = client.get(reverse("api_projects"), req, **h_owner)
388
        body = json.loads(r.content)
389
        self.assertEqual(len(body), 2)
390

    
391
        # Leave failed
392
        status = self.memb_action(m_plain_id, "leave", h_owner)
393
        self.assertEqual(status, 403)
394

    
395
        # Leave
396
        status = self.memb_action(m_plain_id, "leave", h_plain)
397
        self.assertEqual(status, 200)
398

    
399
        # Suspend failed
400
        status = self.project_action(project_id, "suspend", h_owner)
401
        self.assertEqual(status, 403)
402

    
403
        # Unsuspend failed
404
        status = self.project_action(project_id, "unsuspend", h_admin)
405
        self.assertEqual(status, 409)
406

    
407
        # Suspend
408
        status = self.project_action(project_id, "suspend", h_admin)
409
        self.assertEqual(status, 200)
410

    
411
        # Cannot view project
412
        r = client.get(reverse("api_project",
413
                               kwargs={"project_id": project_id}), **h_plain)
414
        self.assertEqual(r.status_code, 403)
415

    
416
        # Unsuspend
417
        status = self.project_action(project_id, "unsuspend", h_admin)
418
        self.assertEqual(status, 200)
419

    
420
        # Cannot approve, project with same name exists
421
        status = self.app_action(project2_app_id, "approve", h_admin)
422
        self.assertEqual(status, 409)
423

    
424
        # Terminate
425
        status = self.project_action(project_id, "terminate", h_admin)
426
        self.assertEqual(status, 200)
427

    
428
        # Join failed
429
        status, _ = self.join(project_id, h_admin)
430
        self.assertEqual(status, 409)
431

    
432
        # Can approve now
433
        status = self.app_action(project2_app_id, "approve", h_admin)
434
        self.assertEqual(status, 200)
435

    
436
        # Join new project
437
        status, body = self.join(project2_id, h_plain)
438
        self.assertEqual(status, 200)
439
        m_project2 = body["id"]
440

    
441
        # Get memberships of project
442
        body = {"body": json.dumps({"project": project2_id})}
443
        r = client.get(reverse("api_memberships"), body, **h_owner)
444
        body = json.loads(r.content)
445
        self.assertEqual(len(body), 1)
446
        self.assertEqual(body[0]["id"], m_project2)
447

    
448
        # Remove member
449
        status = self.memb_action(m_project2, "remove", h_owner)
450
        self.assertEqual(status, 200)
451

    
452
        # Reinstate failed
453
        status = self.project_action(project_id, "reinstate", h_admin)
454
        self.assertEqual(status, 409)
455

    
456
        # Rename
457
        app2_renamed = copy.deepcopy(app2)
458
        app2_renamed["name"] = "new.name"
459
        status, body = self.modify(app2_renamed, project_id, h_owner)
460
        self.assertEqual(status, 201)
461
        app2_renamed_id = body["application"]
462

    
463
        # Get project
464
        r = client.get(reverse("api_project",
465
                               kwargs={"project_id": project_id}), **h_owner)
466
        body = json.loads(r.content)
467
        self.assertEqual(body["application"], app3_id)
468
        self.assertEqual(body["pending_application"], app2_renamed_id)
469
        self.assertEqual(body["state"], "terminated")
470
        assertIn("deactivation_date", body)
471

    
472
        # Get application
473
        r = client.get(reverse("api_application",
474
                               kwargs={"app_id": app2_renamed_id}), **h_plain)
475
        self.assertEqual(r.status_code, 403)
476

    
477
        r = client.get(reverse("api_application",
478
                               kwargs={"app_id": app2_renamed_id}), **h_owner)
479
        self.assertEqual(r.status_code, 200)
480
        body = json.loads(r.content)
481
        self.assertEqual(body["state"], "pending")
482
        self.assertEqual(body["name"], "new.name")
483

    
484
        # Approve (automatically reinstates)
485
        action = json.dumps({"approve": ""})
486
        r = client.post(reverse("api_application_action",
487
                                kwargs={"app_id": app2_renamed_id}),
488
                        action, content_type="application/json", **h_admin)
489
        self.assertEqual(r.status_code, 200)
490

    
491
        # Bad requests
492
        r = client.head(reverse("api_projects"), **h_admin)
493
        self.assertEqual(r.status_code, 400)
494

    
495
        r = client.head(reverse("api_project",
496
                                kwargs={"project_id": 1}), **h_admin)
497
        self.assertEqual(r.status_code, 400)
498

    
499
        r = client.head(reverse("api_applications"), **h_admin)
500
        self.assertEqual(r.status_code, 400)
501

    
502
        r = client.head(reverse("api_memberships"), **h_admin)
503
        self.assertEqual(r.status_code, 400)
504

    
505
        status = self.project_action(1, "nonex", h_owner)
506
        self.assertEqual(status, 400)
507

    
508
        action = json.dumps({"suspend": "", "unsuspend": ""})
509
        r = client.post(reverse("api_project_action",
510
                                kwargs={"project_id": 1}),
511
                        action, content_type="application/json", **h_owner)
512
        self.assertEqual(r.status_code, 400)
513

    
514
        ap = {"owner": "nonex",
515
              "join_policy": "nonex",
516
              "leave_policy": "nonex",
517
              "start_date": "nonex",
518
              "homepage": {},
519
              "max_members": -3,
520
              "resources": [],
521
              }
522

    
523
        status, body = self.create(ap, h_owner)
524
        self.assertEqual(status, 400)
525
        self.assertEqual(body["badRequest"]["message"], "User does not exist.")
526

    
527
        ap["owner"] = self.user1.uuid
528
        status, body = self.create(ap, h_owner)
529
        self.assertEqual(status, 400)
530

    
531
        ap["name"] = "some.name"
532
        status, body = self.create(ap, h_owner)
533
        self.assertEqual(status, 400)
534

    
535
        ap["join_policy"] = "auto"
536
        status, body = self.create(ap, h_owner)
537
        self.assertEqual(status, 400)
538

    
539
        ap["leave_policy"] = "closed"
540
        status, body = self.create(ap, h_owner)
541
        self.assertEqual(status, 400)
542

    
543
        ap["start_date"] = "2013-01-01T0:0Z"
544
        status, body = self.create(ap, h_owner)
545
        self.assertEqual(status, 400)
546

    
547
        ap["end_date"] = "2014-01-01T0:0Z"
548
        status, body = self.create(ap, h_owner)
549
        self.assertEqual(status, 400)
550

    
551
        ap["max_members"] = 0
552
        status, body = self.create(ap, h_owner)
553
        self.assertEqual(status, 400)
554

    
555
        ap["homepage"] = "a.stri.ng"
556
        status, body = self.create(ap, h_owner)
557
        self.assertEqual(status, 400)
558

    
559
        ap["resources"] = {42: 42}
560
        status, body = self.create(ap, h_owner)
561
        self.assertEqual(status, 400)
562

    
563
        ap["resources"] = {"service1.resource11": {"member_capacity": 512}}
564
        status, body = self.create(ap, h_owner)
565
        self.assertEqual(status, 201)
566

    
567
        ap["name"] = "non_domain_name"
568
        status, body = self.create(ap, h_owner)
569
        self.assertEqual(status, 400)
570

    
571
        ap["name"] = "domain.name"
572
        ap.pop("max_members")
573
        status, body = self.create(ap, h_owner)
574
        self.assertEqual(status, 400)
575

    
576
        filters = {"filter": {"state": "nonex"}}
577
        req = {"body": json.dumps(filters)}
578
        r = client.get(reverse("api_projects"), req, **h_owner)
579
        self.assertEqual(r.status_code, 400)
580

    
581
        filters = {"filter": {"nonex": "nonex"}}
582
        req = {"body": json.dumps(filters)}
583
        r = client.get(reverse("api_projects"), req, **h_owner)
584
        self.assertEqual(r.status_code, 400)
585

    
586
        req = {"body": json.dumps({"project": "nonex"})}
587
        r = client.get(reverse("api_applications"), req, **h_owner)
588
        self.assertEqual(r.status_code, 400)
589

    
590
        req = {"body": json.dumps({"project": "nonex"})}
591
        r = client.get(reverse("api_memberships"), req, **h_owner)
592
        self.assertEqual(r.status_code, 400)
593

    
594

    
595
class TestProjects(TestCase):
596
    """
597
    Test projects.
598
    """
599
    def setUp(self):
600
        # astakos resources
601
        self.resource = Resource.objects.create(name="astakos.pending_app",
602
                                                uplimit=0,
603
                                                ui_visible=False,
604
                                                api_visible=False,
605
                                                service_type="astakos")
606

    
607
        # custom service resources
608
        self.resource = Resource.objects.create(name="service1.resource",
609
                                                uplimit=100,
610
                                                service_type="service1")
611
        self.admin = get_local_user("projects-admin@synnefo.org")
612
        self.admin.uuid = 'uuid1'
613
        self.admin.save()
614

    
615
        self.user = get_local_user("user@synnefo.org")
616
        self.member = get_local_user("member@synnefo.org")
617
        self.member2 = get_local_user("member2@synnefo.org")
618

    
619
        self.admin_client = get_user_client("projects-admin@synnefo.org")
620
        self.user_client = get_user_client("user@synnefo.org")
621
        self.member_client = get_user_client("member@synnefo.org")
622
        self.member2_client = get_user_client("member2@synnefo.org")
623

    
624
        quotas.qh_sync_new_users(AstakosUser.objects.all())
625

    
626
    def tearDown(self):
627
        Service.objects.all().delete()
628
        ProjectApplication.objects.all().delete()
629
        Project.objects.all().delete()
630
        AstakosUser.objects.all().delete()
631

    
632
    @im_settings(PROJECT_ADMINS=['uuid1'])
633
    def test_application_limit(self):
634
        # user cannot create a project
635
        r = self.user_client.get(reverse('project_add'), follow=True)
636
        self.assertRedirects(r, reverse('project_list'))
637
        self.assertContains(r, "You are not allowed to create a new project")
638

    
639
        # but admin can
640
        r = self.admin_client.get(reverse('project_add'), follow=True)
641
        self.assertRedirects(r, reverse('project_add'))
642

    
643
    @im_settings(PROJECT_ADMINS=['uuid1'])
644
    def test_ui_visible(self):
645
        dfrom = datetime.now()
646
        dto = datetime.now() + timedelta(days=30)
647

    
648
        # astakos.pending_app ui_visible flag is False
649
        # we shouldn't be able to create a project application using this
650
        # resource.
651
        application_data = {
652
            'name': 'project.synnefo.org',
653
            'homepage': 'https://www.synnefo.org',
654
            'start_date': dfrom.strftime("%Y-%m-%d"),
655
            'end_date': dto.strftime("%Y-%m-%d"),
656
            'member_join_policy': 2,
657
            'member_leave_policy': 1,
658
            'limit_on_members_number': 5,
659
            'service1.resource_uplimit': 100,
660
            'is_selected_service1.resource': "1",
661
            'astakos.pending_app_uplimit': 100,
662
            'is_selected_accounts': "1",
663
            'user': self.user.pk
664
        }
665
        form = forms.ProjectApplicationForm(data=application_data)
666
        # form is invalid
667
        self.assertEqual(form.is_valid(), False)
668

    
669
        del application_data['astakos.pending_app_uplimit']
670
        del application_data['is_selected_accounts']
671
        form = forms.ProjectApplicationForm(data=application_data)
672
        self.assertEqual(form.is_valid(), True)
673

    
674
    @im_settings(PROJECT_ADMINS=['uuid1'])
675
    def test_applications(self):
676
        # let user have 2 pending applications
677
        quotas.update_base_quota([self.user], 'astakos.pending_app', 2)
678

    
679
        r = self.user_client.get(reverse('project_add'), follow=True)
680
        self.assertRedirects(r, reverse('project_add'))
681

    
682
        # user fills the project application form
683
        post_url = reverse('project_add') + '?verify=1'
684
        dfrom = datetime.now()
685
        dto = datetime.now() + timedelta(days=30)
686
        application_data = {
687
            'name': 'project.synnefo.org',
688
            'homepage': 'https://www.synnefo.org',
689
            'start_date': dfrom.strftime("%Y-%m-%d"),
690
            'end_date': dto.strftime("%Y-%m-%d"),
691
            'member_join_policy': 2,
692
            'member_leave_policy': 1,
693
            'service1.resource_uplimit': 100,
694
            'is_selected_service1.resource': "1",
695
            'user': self.user.pk
696
        }
697
        r = self.user_client.post(post_url, data=application_data, follow=True)
698
        self.assertEqual(r.status_code, 200)
699
        self.assertEqual(r.context['form'].is_valid(), False)
700

    
701
        application_data['limit_on_members_number'] = 5
702
        r = self.user_client.post(post_url, data=application_data, follow=True)
703
        self.assertEqual(r.status_code, 200)
704
        self.assertEqual(r.context['form'].is_valid(), True)
705

    
706
        # confirm request
707
        post_url = reverse('project_add') + '?verify=0&edit=0'
708
        r = self.user_client.post(post_url, data=application_data, follow=True)
709
        self.assertContains(r, "The project application has been received")
710
        self.assertRedirects(r, reverse('project_list'))
711
        self.assertEqual(ProjectApplication.objects.count(), 1)
712
        app1 = ProjectApplication.objects.filter().order_by('pk')[0]
713
        app1_id = app1.pk
714
        project1_id = app1.chain_id
715

    
716
        # create another one
717
        application_data['name'] = 'project2.synnefo.org'
718
        r = self.user_client.post(post_url, data=application_data, follow=True)
719
        app2 = ProjectApplication.objects.filter().order_by('pk')[1]
720
        project2_id = app2.chain_id
721

    
722
        # no more applications (LIMIT is 2)
723
        r = self.user_client.get(reverse('project_add'), follow=True)
724
        self.assertRedirects(r, reverse('project_list'))
725
        self.assertContains(r, "You are not allowed to create a new project")
726

    
727
        # one project per application
728
        self.assertEqual(Project.objects.count(), 2)
729

    
730
        # login
731
        self.admin_client.get(reverse("edit_profile"))
732
        # admin approves
733
        r = self.admin_client.post(reverse('project_app_approve',
734
                                           kwargs={'application_id': app1_id}),
735
                                   follow=True)
736
        self.assertEqual(r.status_code, 200)
737

    
738
        Q_ACTIVE = Project.o_state_q(Project.O_ACTIVE)
739
        self.assertEqual(Project.objects.filter(Q_ACTIVE).count(), 1)
740

    
741
        # login
742
        self.member_client.get(reverse("edit_profile"))
743
        # cannot join project2 (not approved yet)
744
        join_url = reverse("project_join", kwargs={'chain_id': project2_id})
745
        r = self.member_client.post(join_url, follow=True)
746

    
747
        # can join project1
748
        self.member_client.get(reverse("edit_profile"))
749
        join_url = reverse("project_join", kwargs={'chain_id': project1_id})
750
        r = self.member_client.post(join_url, follow=True)
751
        self.assertEqual(r.status_code, 200)
752

    
753
        memberships = ProjectMembership.objects.all()
754
        self.assertEqual(len(memberships), 1)
755
        memb_id = memberships[0].id
756

    
757
        reject_member_url = reverse('project_reject_member',
758
                                    kwargs={'memb_id': memb_id})
759
        accept_member_url = reverse('project_accept_member',
760
                                    kwargs={'memb_id': memb_id})
761

    
762
        # only project owner is allowed to reject
763
        r = self.member_client.post(reject_member_url, follow=True)
764
        self.assertContains(r, "You do not have the permissions")
765
        self.assertEqual(r.status_code, 200)
766

    
767
        # user (owns project) rejects membership
768
        r = self.user_client.post(reject_member_url, follow=True)
769
        self.assertEqual(ProjectMembership.objects.any_accepted().count(), 0)
770

    
771
        # user rejoins
772
        self.member_client.get(reverse("edit_profile"))
773
        join_url = reverse("project_join", kwargs={'chain_id': project1_id})
774
        r = self.member_client.post(join_url, follow=True)
775
        self.assertEqual(r.status_code, 200)
776
        self.assertEqual(ProjectMembership.objects.requested().count(), 1)
777

    
778
        # user (owns project) accepts membership
779
        r = self.user_client.post(accept_member_url, follow=True)
780
        self.assertEqual(ProjectMembership.objects.any_accepted().count(), 1)
781
        membership = ProjectMembership.objects.get()
782
        self.assertEqual(membership.state, ProjectMembership.ACCEPTED)
783

    
784
        user_quotas = quotas.get_users_quotas([self.member])
785
        resource = 'service1.resource'
786
        newlimit = user_quotas[self.member.uuid]['system'][resource]['limit']
787
        # 100 from initial uplimit + 100 from project
788
        self.assertEqual(newlimit, 200)
789

    
790
        remove_member_url = reverse('project_remove_member',
791
                                    kwargs={'memb_id': membership.id})
792
        r = self.user_client.post(remove_member_url, follow=True)
793
        self.assertEqual(r.status_code, 200)
794

    
795
        user_quotas = quotas.get_users_quotas([self.member])
796
        resource = 'service1.resource'
797
        newlimit = user_quotas[self.member.uuid]['system'][resource]['limit']
798
        # 200 - 100 from project
799
        self.assertEqual(newlimit, 100)
800

    
801
        # support email gets rendered in emails content
802
        for mail in get_mailbox('user@synnefo.org'):
803
            self.assertTrue(settings.CONTACT_EMAIL in
804
                            mail.message().as_string())