Statistics
| Branch: | Tag: | Revision:

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

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
from snf_django.utils.testing import assertGreater, assertIn, assertRaises
36

    
37

    
38
class ProjectAPITest(TestCase):
39

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
593

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

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

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

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

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

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

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

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

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

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

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

    
673
    @im_settings(PROJECT_ADMINS=['uuid1'])
674
    def test_applications(self):
675
        # let user have 2 pending applications
676
        q = self.user.get_resource_policy('astakos.pending_app')
677
        quotas.update_base_quota(q, 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())