Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (30.6 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
                      "allow_in_projects": True}
49
        r, _ = register.add_resource(resource11)
50
        register.update_resource(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_resource(r, 1024)
58

    
59
        # create user
60
        self.user1 = get_local_user("test@grnet.gr")
61
        quotas.qh_sync_user(self.user1)
62
        self.user2 = get_local_user("test2@grnet.gr")
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")
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
                       "allow_in_projects": False}
77
        r, _ = register.add_resource(pending_app)
78
        register.update_resource(r, 3)
79

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

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

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

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

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

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

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

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

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

    
158
        status = self.memb_action(1, "accept", h_admin)
159
        self.assertEqual(status, 404)
160

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

    
169
        status, body = self.modify(app1, 1, h_owner)
170
        self.assertEqual(status, 404)
171

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

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

    
189
        # Approve forbidden
190
        status = self.app_action(app_id, "approve", h_owner)
191
        self.assertEqual(status, 403)
192

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

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

    
206
        # No more pending allowed
207
        status, body = self.create(app_p3, h_owner)
208
        self.assertEqual(status, 409)
209

    
210
        # Cancel
211
        status = self.app_action(project3_app_id, "cancel", h_owner)
212
        self.assertEqual(status, 200)
213

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

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

    
231
        # Dismiss failed
232
        status = self.app_action(app2_id, "dismiss", h_owner)
233
        self.assertEqual(status, 409)
234

    
235
        # Deny
236
        status = self.app_action(app2_id, "deny", h_admin)
237
        self.assertEqual(status, 200)
238

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

    
244
        # Dismiss
245
        status = self.app_action(app2_id, "dismiss", h_owner)
246
        self.assertEqual(status, 200)
247

    
248
        # Resubmit
249
        status, body = self.modify(app2, project_id, h_owner)
250
        self.assertEqual(status, 201)
251
        app3_id = body["application"]
252

    
253
        # Approve
254
        status = self.app_action(app3_id, "approve", h_admin)
255
        self.assertEqual(status, 200)
256

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

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

    
270
        # Enroll
271
        status, body = self.enroll(project_id, self.user3, h_owner)
272
        self.assertEqual(status, 200)
273
        m_plain_id = body["id"]
274

    
275
        # Join
276
        status, body = self.join(project_id, h_owner)
277
        self.assertEqual(status, 200)
278
        memb_id = body["id"]
279

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

    
288
        r = client.get(reverse("api_memberships"), **h_owner)
289
        body = json.loads(r.content)
290
        self.assertEqual(len(body), 2)
291

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

    
301
        r = client.get(reverse("api_membership", kwargs={"memb_id": memb_id}),
302
                       **h_plain)
303
        self.assertEqual(r.status_code, 403)
304

    
305
        status = self.memb_action(memb_id, "leave", h_admin)
306
        self.assertEqual(status, 409)
307

    
308
        status = self.memb_action(memb_id, "cancel", h_owner)
309
        self.assertEqual(status, 200)
310

    
311
        status, body = self.join(project_id, h_owner)
312
        self.assertEqual(status, 200)
313
        self.assertEqual(memb_id, body["id"])
314

    
315
        status = self.memb_action(memb_id, "reject", h_owner)
316
        self.assertEqual(status, 200)
317

    
318
        status, body = self.join(project_id, h_owner)
319
        self.assertEqual(status, 200)
320
        self.assertEqual(memb_id, body["id"])
321

    
322
        status = self.memb_action(memb_id, "accept", h_owner)
323
        self.assertEqual(status, 200)
324

    
325
        # Enroll fails, already in
326
        status, body = self.enroll(project_id, self.user1, h_owner)
327
        self.assertEqual(status, 409)
328

    
329
        # Remove member
330
        status = self.memb_action(memb_id, "remove", h_owner)
331
        self.assertEqual(status, 200)
332

    
333
        # Enroll a removed member
334
        status, body = self.enroll(project_id, self.user1, h_owner)
335
        self.assertEqual(status, 200)
336

    
337
        # Remove member
338
        status = self.memb_action(memb_id, "remove", h_owner)
339
        self.assertEqual(status, 200)
340

    
341
        # Re-join
342
        status, body = self.join(project_id, h_owner)
343
        self.assertEqual(status, 200)
344
        self.assertEqual(memb_id, body["id"])
345

    
346
        # Enroll a requested member
347
        status, body = self.enroll(project_id, self.user1, h_owner)
348
        self.assertEqual(status, 200)
349

    
350
        # Enroll fails, already in
351
        status, body = self.enroll(project_id, self.user1, h_owner)
352
        self.assertEqual(status, 409)
353

    
354
        # Enroll fails, project does not exist
355
        status, body = self.enroll(-1, self.user1, h_owner)
356
        self.assertEqual(status, 409)
357

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

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

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

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

    
388
        # Leave failed
389
        status = self.memb_action(m_plain_id, "leave", h_owner)
390
        self.assertEqual(status, 403)
391

    
392
        # Leave
393
        status = self.memb_action(m_plain_id, "leave", h_plain)
394
        self.assertEqual(status, 200)
395

    
396
        # Suspend failed
397
        status = self.project_action(project_id, "suspend", h_owner)
398
        self.assertEqual(status, 403)
399

    
400
        # Unsuspend failed
401
        status = self.project_action(project_id, "unsuspend", h_admin)
402
        self.assertEqual(status, 409)
403

    
404
        # Suspend
405
        status = self.project_action(project_id, "suspend", h_admin)
406
        self.assertEqual(status, 200)
407

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

    
413
        # Unsuspend
414
        status = self.project_action(project_id, "unsuspend", h_admin)
415
        self.assertEqual(status, 200)
416

    
417
        # Cannot approve, project with same name exists
418
        status = self.app_action(project2_app_id, "approve", h_admin)
419
        self.assertEqual(status, 409)
420

    
421
        # Terminate
422
        status = self.project_action(project_id, "terminate", h_admin)
423
        self.assertEqual(status, 200)
424

    
425
        # Join failed
426
        status, _ = self.join(project_id, h_admin)
427
        self.assertEqual(status, 409)
428

    
429
        # Can approve now
430
        status = self.app_action(project2_app_id, "approve", h_admin)
431
        self.assertEqual(status, 200)
432

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

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

    
445
        # Remove member
446
        status = self.memb_action(m_project2, "remove", h_owner)
447
        self.assertEqual(status, 200)
448

    
449
        # Reinstate failed
450
        status = self.project_action(project_id, "reinstate", h_admin)
451
        self.assertEqual(status, 409)
452

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

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

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

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

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

    
488
        # Bad requests
489
        r = client.head(reverse("api_projects"), **h_admin)
490
        self.assertEqual(r.status_code, 400)
491

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

    
496
        r = client.head(reverse("api_applications"), **h_admin)
497
        self.assertEqual(r.status_code, 400)
498

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

    
502
        status = self.project_action(1, "nonex", h_owner)
503
        self.assertEqual(status, 400)
504

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

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

    
520
        status, body = self.create(ap, h_owner)
521
        self.assertEqual(status, 400)
522
        self.assertEqual(body["badRequest"]["message"], "User does not exist.")
523

    
524
        ap["owner"] = self.user1.uuid
525
        status, body = self.create(ap, h_owner)
526
        self.assertEqual(status, 400)
527

    
528
        ap["name"] = "some.name"
529
        status, body = self.create(ap, h_owner)
530
        self.assertEqual(status, 400)
531

    
532
        ap["join_policy"] = "auto"
533
        status, body = self.create(ap, h_owner)
534
        self.assertEqual(status, 400)
535

    
536
        ap["leave_policy"] = "closed"
537
        status, body = self.create(ap, h_owner)
538
        self.assertEqual(status, 400)
539

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

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

    
548
        ap["max_members"] = 0
549
        status, body = self.create(ap, h_owner)
550
        self.assertEqual(status, 400)
551

    
552
        ap["homepage"] = "a.stri.ng"
553
        status, body = self.create(ap, h_owner)
554
        self.assertEqual(status, 400)
555

    
556
        ap["resources"] = {42: 42}
557
        status, body = self.create(ap, h_owner)
558
        self.assertEqual(status, 400)
559

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

    
564
        ap["name"] = "non_domain_name"
565
        status, body = self.create(ap, h_owner)
566
        self.assertEqual(status, 400)
567

    
568
        ap["name"] = "domain.name"
569
        ap.pop("max_members")
570
        status, body = self.create(ap, h_owner)
571
        self.assertEqual(status, 400)
572

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

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

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

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

    
591

    
592
class TestProjects(TestCase):
593
    """
594
    Test projects.
595
    """
596
    def setUp(self):
597
        # astakos resources
598
        self.resource = Resource.objects.create(name="astakos.pending_app",
599
                                                uplimit=0,
600
                                                allow_in_projects=False,
601
                                                service_type="astakos")
602

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

    
611
        self.user = get_local_user("user@synnefo.org")
612
        self.member = get_local_user("member@synnefo.org")
613
        self.member2 = get_local_user("member2@synnefo.org")
614

    
615
        self.admin_client = get_user_client("projects-admin@synnefo.org")
616
        self.user_client = get_user_client("user@synnefo.org")
617
        self.member_client = get_user_client("member@synnefo.org")
618
        self.member2_client = get_user_client("member2@synnefo.org")
619

    
620
        quotas.qh_sync_users(AstakosUser.objects.all())
621

    
622
    def tearDown(self):
623
        Service.objects.all().delete()
624
        ProjectApplication.objects.all().delete()
625
        Project.objects.all().delete()
626
        AstakosUser.objects.all().delete()
627

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

    
635
        # but admin can
636
        r = self.admin_client.get(reverse('project_add'), follow=True)
637
        self.assertRedirects(r, reverse('project_add'))
638

    
639
    @im_settings(PROJECT_ADMINS=['uuid1'])
640
    def test_allow_in_project(self):
641
        dfrom = datetime.now()
642
        dto = datetime.now() + timedelta(days=30)
643

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

    
665
        del application_data['astakos.pending_app_uplimit']
666
        del application_data['is_selected_accounts']
667
        form = forms.ProjectApplicationForm(data=application_data)
668
        self.assertEqual(form.is_valid(), True)
669

    
670
    @im_settings(PROJECT_ADMINS=['uuid1'])
671
    def test_applications(self):
672
        # let user have 2 pending applications
673
        quotas.add_base_quota(self.user, 'astakos.pending_app', 2)
674

    
675
        r = self.user_client.get(reverse('project_add'), follow=True)
676
        self.assertRedirects(r, reverse('project_add'))
677

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

    
697
        application_data['limit_on_members_number'] = 5
698
        r = self.user_client.post(post_url, data=application_data, follow=True)
699
        self.assertEqual(r.status_code, 200)
700
        self.assertEqual(r.context['form'].is_valid(), True)
701

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

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

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

    
723
        # one project per application
724
        self.assertEqual(Project.objects.count(), 2)
725

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

    
734
        Q_ACTIVE = Project.o_state_q(Project.O_ACTIVE)
735
        self.assertEqual(Project.objects.filter(Q_ACTIVE).count(), 1)
736

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

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

    
749
        memberships = ProjectMembership.objects.all()
750
        self.assertEqual(len(memberships), 1)
751
        memb_id = memberships[0].id
752

    
753
        reject_member_url = reverse('project_reject_member',
754
                                    kwargs={'memb_id': memb_id})
755
        accept_member_url = reverse('project_accept_member',
756
                                    kwargs={'memb_id': memb_id})
757

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

    
763
        # user (owns project) rejects membership
764
        r = self.user_client.post(reject_member_url, follow=True)
765
        self.assertEqual(ProjectMembership.objects.any_accepted().count(), 0)
766

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

    
774
        # user (owns project) accepts membership
775
        r = self.user_client.post(accept_member_url, follow=True)
776
        self.assertEqual(ProjectMembership.objects.any_accepted().count(), 1)
777
        membership = ProjectMembership.objects.get()
778
        self.assertEqual(membership.state, ProjectMembership.ACCEPTED)
779

    
780
        user_quotas = quotas.get_users_quotas([self.member])
781
        resource = 'service1.resource'
782
        newlimit = user_quotas[self.member.uuid]['system'][resource]['limit']
783
        # 100 from initial uplimit + 100 from project
784
        self.assertEqual(newlimit, 200)
785

    
786
        remove_member_url = reverse('project_remove_member',
787
                                    kwargs={'memb_id': membership.id})
788
        r = self.user_client.post(remove_member_url, follow=True)
789
        self.assertEqual(r.status_code, 200)
790

    
791
        user_quotas = quotas.get_users_quotas([self.member])
792
        resource = 'service1.resource'
793
        newlimit = user_quotas[self.member.uuid]['system'][resource]['limit']
794
        # 200 - 100 from project
795
        self.assertEqual(newlimit, 100)
796

    
797
        # support email gets rendered in emails content
798
        for mail in get_mailbox('user@synnefo.org'):
799
            self.assertTrue(settings.CONTACT_EMAIL in
800
                            mail.message().as_string())