Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: finish and do write portfolio #34

Merged
merged 11 commits into from
Nov 3, 2024
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,7 @@ data
certbot
certs
localhost.crt
localhost.key
localhost.key
.vscode
venv
.env
50 changes: 18 additions & 32 deletions ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,38 +19,24 @@
- test : 테스트 코드 수정에 대한 커밋


## Must
0. docker compose 빌드 시간 줄이기 (해결)
issue: 메모리 문제였음
1. certbot을 활용하여 HTTPS 강화하기(해결)
issue: docker-compose.yml: 볼륨 마운트 문제 + 인증서 발급 최적화 문제
2. Docker-compose를 활용하여 로컬에서 PostgreSQL 사용하기(해결)
- docker-compose.yml 과 nginx.conf 개발환경 배포환경 분리하기
- 초기 잘못 생성한 db의 이름이 postgres였다 이게 문제였음

3. github actions를 활용한 테스트 자동화 및 배포 자동화(해결)
- sudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper
sudo: a password is required: sudo visudo -> add ehgus_dev_8621 ALL=(ALL) NOPASSWD:ALL
Error: Process completed with exit code 1.
- [email protected]: Permission denied (publickey).
Error: Process completed with exit code 255.:nano /etc/ssh/sshd_config -> add PasswordAuthentication yes, check AuthorizedKeysFile, 나는 파일이름 잘 못 지은것이 문제
- ssh -i path/to/private_key -o "StrictHostKeyChecking=no" username@server-ip 로컬 접속 테스트
4. 이미지 스토리지 분리하기(해결)
- 포트폴리오 용이므로 비용적 비효율성으로 스킵
- 이미지 스토리지로 이전하는 방법을 블로그에 포스팅 하기
5. 결제 유닛 테스트 작성하고 자동화하기
6. 결제 통합 테스트 작성하고 자동화하기
7. 결제시 정보 업데이트가 늦는 지점 찾아서 해결하기


## alpha
1. 환불 로직 구현하기
2. Celery를 활용하여 구매확정 스케줄링하기





## notice
1. ci-cd 자동화 끝
2. 필요한 것만 테스트하면 됨
3. 배포가 안되는 것은 당연한 것 배포하면서 테스트할 필요없음
4. gcp로 배포중인 것 삭제(비용문제)

-----------------------------------------------------------
해결해야할 문제

1. cart, ProductItemId issue
2. 포트폴리오: cloud를 사용하지 않기 때문에 local에서 docker를 통해 서버를 실행시키기
1) 프로젝트개요
2) 기술 스택
3) 요구사항
4) 테스트 기술서()
5) 서비스 아키텍쳐
6) 소스코드 관리
7) 트러블 슈팅: 결제 및 환불, 성능 및 부하, 비용



File renamed without changes.
4 changes: 2 additions & 2 deletions client/src/api/axiosInstance.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import axios from "axios";

export const axiosInstance = axios.create({
// baseURL: "http://localhost/", //개 발용 https,
baseURL: "https://www.iseul.org/",
baseURL: "http://localhost/", //개 발용 https,
// baseURL: "https://www.iseul.org/",
headers: { "Content-Type": "application/json" },
withCredentials: true,
});
Expand Down
1 change: 0 additions & 1 deletion docker-compose.override.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ services:
python manage.py migrate --settings=config.settings.local &&
gunicorn config.wsgi:application --bind 0.0.0.0:8000 --timeout 120 --workers 6"


client:
build:
context: ./client
Expand Down
10 changes: 7 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ services:
volumes:
- ./data/db:/var/lib/postgresql/data
environment:
POSTGRES_DB: iseul
POSTGRES_USER: admin
POSTGRES_DB: postgres
POSTGRES_USER: postgres
POSTGRES_PASSWORD: "!l1185216"

server:
Expand All @@ -21,6 +21,7 @@ services:
- "8000:8000"
depends_on:
- db
- cache
expose:
- "8000"

Expand All @@ -33,4 +34,7 @@ services:
- server
command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"


cache:
image: redis
ports:
- "6379:6379"
Binary file added server/.coverage
Binary file not shown.
2 changes: 1 addition & 1 deletion server/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
path('user/', include('user.urls')),
path('kakaopay/', include('kakaopay.urls')),
path('purchase/', include('purchase.urls')),
path('cart/', include('cart.urls'))
path('cart/', include('cart.urls', namespace='cart'))
]

schema_view = get_schema_view(
Expand Down
18 changes: 9 additions & 9 deletions server/cart/serializers/cart.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@ def get_sizes_with_count(self, obj):


class CartItemSerializer(serializers.ModelSerializer):
productItemId = ProductSerializer(read_only=True)

productItemId = serializers.PrimaryKeyRelatedField(
queryset=Product.objects.all(), write_only=True, source='product'
)
product = ProductSerializer(read_only=True) # Read-only product representation

class Meta:
model = CartItem
fields = ['id', 'cart', 'productItemId', 'quantity', 'size']
fields = ['id', 'cart', 'productItemId','product', 'quantity', 'size']
extra_kwargs = {'cart': {'read_only': True}}

def create(self, validated_data):
Expand All @@ -41,12 +43,10 @@ def create(self, validated_data):
cart, _ = Cart.objects.get_or_create(user=user)
validated_data['cart'] = cart

product_id = self.context['request'].data.get('productItemId')
if product_id:
# Product 인스턴스 찾기
product = Product.objects.get(id=product_id)
# CartItem에 Product 할당
validated_data['productItemId'] = product
validated_data['productItemId'] = validated_data.pop('product')



return super().create(validated_data)


Expand Down
Empty file added server/cart/tests/__init__.py
Empty file.
53 changes: 53 additions & 0 deletions server/cart/tests/test_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from django.urls import reverse
from rest_framework.test import APITestCase, APIClient
from rest_framework import status
from rest_framework.request import Request
from django.contrib.auth import get_user_model
from products.models.product import Product, Size, ProductSize
from cart.models.cart import Cart, CartItem

User = get_user_model()

class CartAPITestCase(APITestCase):
def setUp(self):
self.user = User.objects.create_user(email='[email protected]', username="testuser", password="12345")
self.client = APIClient()
self.client.force_authenticate(user=self.user)

self.product = Product.objects.create(name='Test Product', price=100.00, category='outer', image_url='www.test-server.com/image-url')
self.size = Size.objects.create(size='L')
self.product_size = ProductSize.objects.create(product=self.product, size=self.size, count=10)

self.cart = Cart.objects.create(user=self.user)
self.cart_item = CartItem.objects.create(cart=self.cart, productItemId=self.product, quantity=2, size='L')

def test_get_cart(self):
url = reverse('api:cart:cart')
response = self.client.get(url)

self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIn('items', response.data)
self.assertEqual(len(response.data['items']), 1)

def test_add_item_to_cart(self):
url = reverse('api:cart:cart')
data = {
'productItemId': self.product.id,
'quantity': 1,
'size': 'L'
}

response = self.client.post(url, data, format='json')

print(response) # ÀÀ´ä µ¥ÀÌÅ͸¦ È®ÀÎÇϱâ À§ÇÑ Ãâ·Â

self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.data['quantity'], 1)
self.assertEqual(response.data['size'], 'L')

def test_delete_item_from_cart(self):
url = reverse('api:cart:cart-item-delete', args=[self.cart_item.pk])
response = self.client.delete(url)

self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
self.assertFalse(CartItem.objects.filter(pk=self.cart_item.pk).exists())
30 changes: 30 additions & 0 deletions server/cart/tests/test_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from django.test import TestCase
from django.contrib.auth import get_user_model
from products.models.product import Product
from cart.models.cart import Cart, CartItem

User = get_user_model()

class CartAndItemTests(TestCase):
def setUp(self):
self.user = User.objects.create_user(email='[email protected]', username='testuser', password='12345')
self.product = Product.objects.create(name="Sample product", price=100.00)
self.cart = Cart.objects.create(user=self.user)
self.cart_item = CartItem.objects.create(cart=self.cart, productItemId=self.product, quantity=2, size='M')

def test_cart_creation(self):
self.assertEqual(self.cart.user, self.user)

# def test_cart_str(self):
# self.assertEqual(str(self.cart))

def test_update_cart_item(self):
self.cart_item.quantity = 3
self.cart_item.save()
self.assertEqual(CartItem.objects.get(id=self.cart_item.id).quantity, 3)

def test_delete_cart_item(self):
cart_item_id = self.cart_item.id
self.cart_item.delete()
with self.assertRaises(CartItem.DoesNotExist):
CartItem.objects.get(id=cart_item_id)
66 changes: 66 additions & 0 deletions server/cart/tests/test_sz.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from django.test import TestCase
from rest_framework.test import APIClient, APIRequestFactory, force_authenticate
from rest_framework.request import Request
from django.contrib.auth import get_user_model
from products.models.product import Product, ProductSize, Size
from cart.models.cart import Cart, CartItem
from cart.serializers.cart import CartSerializer, CartItemSerializer, ProductSerializer, ProductSizeSerializer

User = get_user_model()

class SerailizerTestCase(TestCase):
def setUp(self):
self.user = User.objects.create_user(email="[email protected]", username="testuser", password="12345")
self.factory = APIRequestFactory()
self.request = self.factory.get('/')
force_authenticate(self.request, self.user)
self.drf_request = Request(self.request)


self.product = Product.objects.create(name='Test Product', price=100.00, category='Outer', image_url='http://example.com/test.jpg')
self.size = Size.objects.create(size='L')
self.product_size = ProductSize.objects.create(product=self.product, size=self.size, count=10)
self.cart = Cart.objects.create(user=self.user)
self.cart_item = CartItem.objects.create(cart=self.cart, productItemId=self.product, quantity=2, size = 'L')

def test_product_size_serializer(self):
serializer = ProductSizeSerializer(instance=self.product_size)
data = serializer.data
self.assertEqual(data['size_name'], 'L')
self.assertEqual(data['size_count'], 10)

def test_product_serializer(self):
serializer = ProductSerializer(instance=self.product)
data=serializer.data
self.assertIn('sizes_with_count', data)
self.assertIsInstance(data['sizes_with_count'], list)

if data['sizes_with_count']:
expected_size_data = {'size': self.size.id, 'size_name': 'L', 'size_count': 10}
self.assertIn(expected_size_data, [dict(s) for s in data['sizes_with_count']])

def test_cart_item_serializer_create(self):
data = {
'productItemId': self.product.id,
'quantity': 1,
'size': 'L'
}
context = {'request': self.drf_request}

serializer = CartItemSerializer(data=data, context=context)


if serializer.is_valid():
cart_item = serializer.save()
self.assertIsNotNone(cart_item)
self.assertEqual(cart_item.cart, self.cart)
self.assertEqual(cart_item.quantity, 1)
self.assertEqual(cart_item.size, 'L')
else:
print(serializer.errors)


def test_cart_serializer(self):
serializer = CartSerializer(instance=self.cart)
data = serializer.data
self.assertEqual(len(data['items']), 1)
1 change: 0 additions & 1 deletion server/cart/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

app_name = 'cart'


urlpatterns = [
path('', CartView.as_view(), name='cart'),
path('<int:pk>/', CartItemDeleteView.as_view(), name='cart-item-delete'),
Expand Down
Loading
Loading